如何创建一个小的Javascript扩展语言?

时间:2012-07-14 18:08:07

标签: javascript coffeescript namespaces closures

我最近重构了我的JS代码并偶然发现了这种模式:

APP = (function() {
  var x,y,z;
  function foo() {}
  function bar() {}
  return {x:x, y:y, z:z, foo:foo: bar:bar};
})();

这样做的好处是它创建了非全局变量,其中的函数可以访问APP中定义的所有内容。因此,APP.foo可以访问x, y, z,并且无需键入APP.bar()APP.x等即可停用。还可以使用APP.bar()APP.x全局访问所有内容等等你也可以嵌套它们:

APP = (function() {
  var x,y,z;
  function foo() {}
  function bar() {}

  var WIDGETS = (function() {
    var a,b,c;
    function hello() {}
    function world() {}
    return {a:a, b:b, c:c, hello:hello, world:world};
  })();

  return {x:x, y:y, z:z, foo:foo: bar:bar, WIDGETS:WIDGETS};
})();

因此WIDGETS可以访问APP中的变量,但反之亦然(APP.WIDGETS.hello可以使用foo(),但APP.foo必须使用{{} 1}})。

我尝试使用ERB(我在Rails上)创建这个模式,但结果却很混乱。所以我正在考虑为此编写一个小的源到源编译器 - 像CoffeeScript(具有SASS的最小差异/扩展语言哲学),它只是将一些函数编译成替代的javascript。

我只想要一个速记。

例如,这将编译到我上面的第二个代码块:

WIDGETS.hello()

简单和小 - 只是因此您​​不需要跟踪变量。还希望将命名空间分开(因此我可以将其拆分为多个文件):

//NAMESPACE is a magical function I compile down to the long version in the second code block
APP = NAMESPACE(function() {
  var x,y,z;
  function foo() {}
  function bar() {}

  var WIDGETS = NAMESPACE(function() {
    var a,b,c;
    function hello() {}
    function world() {}
    //**notice the missing return statement that I don't need to manage and map to my variables**
  });
  //**notice the missing return statement that I don't need to manage and map to my variables**
});

有关如何做到这一点的任何想法?我不确定一切,但我认为如果存在,我会更喜欢Javascript。

2 个答案:

答案 0 :(得分:1)

您可能想要了解的事情:

还有一项关于EcmaScript 6中模块系统的建议,将来可以使用:http://wiki.ecmascript.org/doku.php?id=harmony:modules

现在,如果目标只是输入foo()而不是APP.foo(),那么我认为创建javascript的语言超集有点紧张......

如果您使用CoffeeScript导出,那么变量就不那么详细了:

APP = do ->
  [x,y,z] = []
  foo = ->
  bar = ->

  WIDGETS = do ->
    [a,b,c] = []
    hello = ->
    world = ->
    { a, b, c, hello, world }

  { x, y, z, foo, bar, WIDGETS }

在实践中,你很少导出每个变量,事实上你们中的一些变量是“私有的”。您还可以在对象内部定义函数:

APP = (function() {
  var x,y,z;

  var WIDGETS = (function() {
    var a,b,c;
    return {
      hello: function hello(){},
      world: function world(){}
    }

  })();

  return {
    foo: function foo(){},
    bar: function bar(){},
    widgets: WIDGETS
  }

})();

答案 1 :(得分:0)

您所描述的是通常所谓的“模块模式”。这很好,因为您可以定义“隐藏”且无法访问的内部方法和变量。它也不会污染全局命名空间。您可以阅读更多相关信息here(我相信这是关于该技术的第一篇文章之一。)另外herehere

然而,既然你把所有东西都“公之于众”,那就不那么有用了。你可以这样做(包括测试代码):

APP = function (me) {
      me.x = 0;
      me.y = 0;
      me.z = 0;
      me.foo = function () { alert(me.x); },
      me.bar = function () {};

      WIDGETS = function (me2) {
        me2.a = 0;
        me2.b = 0;
        me2.c = 0;
        me2.hello = function () {};
        me2.world = function () {};
        return me2;
      }({});

      return me;
   }({});

APP.x = 1;
APP.foo();

使用较少的语法可以获得相同的效果。

http://jsfiddle.net/ZwCUh/


作为旁注,有一些方法可以在没有编译器的情况下编写代码,使您能够通过原型接近您所寻找的内容。一个很好的例子是jQueryUI的源代码。我确信其他库(原型,moo工具等)也可以用作示例。