创建JavaScript命名空间的最佳方法是什么?

11

我还没有在互联网上找到一个常见的方法来在JavaScript中创建命名空间。

创建命名空间的最佳方式是什么(并列出该特定方法可能存在的任何缺点)。

8个回答

7
(function() {

    var usefulVariable;

    var foo = {
      bar:function(){ alert('hi') }
    };

    // leak into global namespace
    window.foo = foo;

})();

只有foo暴露在全局命名空间中,并且它“存在于”您的私有自执行匿名函数命名空间中。


1
只需调用foo.bar即可。如果我没有通过window.foo公开它,那么foo只能在该函数内部访问。 - meder omuraliev
1
一旦您设置window.<任何东西>,就会破坏使用命名空间的目的。 - Dan Breslau
1
@Dan - 你不必要公开它,我只是在演示这个的某些方面。 - meder omuraliev
为什么不使用this.foo=foo,因为window是非标准的,在这种情况下你已经在全局范围内了?同样,为什么不使用this.foo = {...而不是var foo = {...呢? - Dagg Nabbit
“泄漏到全局命名空间” - 当你故意这样做时,这不是一个泄漏。 - nnnnnn
显示剩余4条评论

7
在JavaScript中,只能通过对象字面量实现命名空间,可以像这样简单地实现:
var MyNS = {
    f1: function() {...},
    f2: function() {...}
};

正如其他人试图展示的那样,如果你想在你的命名空间中提供一个私有作用域(建议这样做),以下方法是最合适的:

var MyNS = (function() {
    var private1 = "...", 
        private2 = "...";

    return {
        f1: function() {...},
        f2: function() {...}
    }
})();

2
“命名空间只能通过对象字面量实现” - 这并不正确。对象字面量只是创建对象的一种方式,但其他创建对象的方法也可以用于“命名空间”目的。 - nnnnnn
你有例子吗? - Justin Johnson
你可以使用构造函数(使用_new_调用),或者使用_new Object_创建一个空对象,然后逐个添加属性。(我并不是说这些技术比使用对象字面量更好,我只是反对你关于必须使用对象字面量的说法。) - nnnnnn

4
书籍《Pro JavaScript Design Patterns》(Harmes & Diaz,2008)提供了使用简单对象字面量或自执行匿名函数创建命名空间的示例,这两种方法都在Justin Johnson的优秀答案中有所说明,这些方法运作良好。
根据情况,可能已经存在并包含成员的命名空间。在这种情况下,这些方法将破坏命名空间中的任何成员。如果这是一个问题,可以使用类似于这样的方法来保留任何现有成员,并添加新成员:
var myNamespace = (function(ns) {
    ns.f1 = function() { ... };
    ns.f2 = function() { ... };

    return ns;
})(window.myNamespace || {});

在这里,myNamespace被赋值为匿名自执行函数返回的值,该函数接收参数ns。正如大家所看到的,该参数的值要么是window.myNamespace,要么是一个空对象,具体取决于myNamespace是否已经被声明。


1
这是我最喜欢的方法。它有点不正统,但效果很好。
  var MyNamespace = new function(){

    this.MY_CONST_ONE = 1;
    this.MY_CONST_TWO = 2;

    this.MyClass = function (x) {
      this.x = x;
    }

    this.MyOtherClass = function (y) {
      this.y = y;
    }

  } // end of namespace

  // ... 

  var oc = new MyNamespace.MyOtherClass(123);

有趣的是,闭包函数使用 new 而不是普通括号进行调用,因此直接在其中,this 指的是函数返回的对象,换句话说,就是“命名空间”本身。


1

我通常尽量保持简单,创建一个表示命名空间的全局对象。如果您正在使用多个脚本,每个脚本都声明命名空间,因为它们可能在不同的应用程序中使用,那么您可能需要检查对象是否已经存在。

//create the namespace - this will actually obliterate any pre-existing 
//object with the same name in the global namespace 
//(and this does happen). 
//NB ommiting the var keyword automatically chucks the object
//variable into the global namespace - which ordinarily you
//don't want to do 
MyNameSpace = {};

//and then create a class/object in the namespace
MyNameSpace.MyClass = {

            someProperty: function(){
                //do something

            },
            anotherProperty: function(){
                //doing something else
            }


};

这基本上就是其他人所说的。我只是提供了一个非常简单的方法。我看到的主要缺点是理论上“MyNameSpace”对象可能已经存在 - 因此在创建它之前可能需要检查一下。通常,如果我要做比那更复杂的事情,我会开始考虑像JQuery或ExtJS这样的框架,这些框架可以大大减轻这些问题的痛苦。

还有一点需要注意的是,你不能找到Javascript中许多事情的共同方法,比如创建命名空间,部分原因是在Javascript中总有不止一种方法来解决问题。大多数语言对于如何做事情都很规范 - 创建类,是函数式的还是面向对象的或过程化的。另一方面,Javascript缺少关键字,高度灵活。这可能是好事或坏事 - 取决于你的观点。但我很喜欢它。


1
要在脚本之间共享代码,您必须至少拥有一个全局变量(除非您想做一些愚蠢的事情,例如将变量附加到已存在的对象(如window.Object)中)。
即使使用模块模式,您也需要有一个共享代码的公共位置,因此,比如说您有三个脚本:
  • core.js
  • utils.js

接下来,我们可以在core.js脚本文件中创建一个模块:

(function(global) {
    var coreModule = {};

    var MY_CONSTANT = 42

    var meaningOfLife = function() {
        return MY_CONSTANT;
    };
    coreModule.meaningOfLife = meaningOfLife;

    global.myRootNamespace = coreModule;
}(this));

utils.js 文件中:
(function(root) {
    var utilsModule = root.utils = {};

    utilsModule.bark = function() {
        console.log("WOOF!");
    };
}(myRootNamespace));

在这里,我们看到了增强模块模式的使用,并根据功能的性质对功能进行命名空间。该特定实现覆盖了myRootNamespace对象上的utils属性的值。但是,我们可以编写代码,使其不会这样做。
(function(root) {
    var utilsModule = root.utils = root.utils || {};

    utilsModule.bark = function() {
        console.log("WOOF!");
    };
}(myRootNamespace));

如果不想使用模块模式,我们可以使用一个简单的函数以非破坏性的方式为我们定义命名空间:

var ensureNamespace = function recur(ns, root) {
    var parts = typeof ns === 'string' ? ns.split('.') : ns;

    var r = root || this;

    if (parts[0]) {
        var next = parts.shift()
        r[next] = r[next] || {};
        return recur(parts, r[next]);
    }

    return r;
};

// maybe another file:
(function(){
    var baz = ensureNamespace("foo.bar.baz");
    // baz === window.foo.bar.baz;

    baz.qux = "Yep...";
}());

查看这篇优秀文章以获取更多见解。


0

你可以结合目前为止收到的答案,取得更大的成就;

var Foo;  // declare in window scope.

(function(){

   var privateVar = "BAR!";

   // define in scope of closure where you can make a mess that 
   // doesn't leak into the window scope.

   Foo = {
      staticFunc: function(){ return privateVar; }
   };

})();

我通常避免使用window.xxxx = yyyy;,因为它不总是与Visual Studio Intellesense兼容。可能还有其他问题,但我不知道。


0

我相信模块模式已经证明了它是正确的选择。对象字面量命名空间将所有内容暴露给命名空间外的调用者代码,这本质上不是一个很好的想法。

如果你坚持创建对象字面量命名空间,请阅读这篇教程。我认为它简单易懂,而且非常全面:JavaScript中的对象字面量命名空间(OLN)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接