JavaScript中是否有一种“简洁”的命名空间方式?

15

我经常遇到将所有JavaScript代码放入类似于命名空间结构中的网站:

namespaces = { com : { example: { example.com's data} }
然而,与其他命名空间框架安全地设置这一点似乎需要相当大量的代码(定义为> 2行)。我想知道是否有简明的方法来做到这一点?此外,是否有相对标准/一致的结构方式?例如,com 命名空间直接附加到全局对象还是通过命名空间对象附加?
[编辑:糟糕,显然 { com = { ... } }不会实现任何接近我想要的效果,感谢 Shog9 指出这一点。]
8个回答

19

Javascript没有独立的命名空间。它有函数,可以为解析名称提供作用域,以及对象,可以为给定作用域中可访问的命名数据做出贡献。

以下是您的示例,已经纠正:

var namespaces = { com: { example: { /* example.com's data */ } } }

这是一个变量namespaces被分配了一个对象字面量。该对象包含一个属性:com,一个拥有一个属性example的对象,这个对象可能会包含一些有趣的内容。

所以,你可以输入类似于namespaces.com.example.Example的一些属性或函数,它都可以工作。当然,这也很荒谬。你没有一个层次化的命名空间,你只是有一个包含着你真正关心的对象的对象。

var com_example_data = { /* example.com's data */ };

这样做同样有效,且不需要无意义的层级结构。

现在,如果您确实想要建立一个层级结构,您可以尝试类似以下的方式:

com_example = com_example || {};
com_example.flags = com_example.flags || { active: false, restricted: true};

com_example.ops = com_example.ops || (function()
    {
       var launchCodes = "38925491753824"; // hidden / private
       return {
         activate: function() { /* ... */ },
         destroyTheWorld: function() { /* ... */ }
       };
    })();

...就我个人而言,这相当简明扼要。


1
尽管现在com_example将成为全局对象的一部分。假设我们添加了许多其他对象,如com_something_elsecom_etc,那么我们仍然会污染全局对象的根级别。如果我们只有一个全局的com对象,并将所有其他对象添加到其中,这不是更好吗?此外,我们不希望覆盖任何现有对象,如果正在使用多个库,该怎么办? - Peter
2
@Peter:如果多个库定义了相同的符号,无论如何都会出现问题;这就是为什么像jQuery这样的库要尽可能地将所有内容塞到一个单一的全局对象中的原因。我的观点不是你应该使用多个顶级对象,而是使用深度嵌套的对象来伪造命名空间并不能比使用疯狂长的对象名称更有优势。请参见我的最后一个示例以获取更实用的方法:使用一个不太可能发生冲突的单一全局名称,然后使用一种允许不同代码片段向其中添加对象的技术。 - Shog9

12

这是Peter Michaux关于Javascript命名空间的有趣文章。他讨论了3种不同类型的Javascript命名空间:

  1. 前缀命名空间
  2. 单个对象命名空间
  3. 嵌套对象命名空间

我不会剽窃他在这里所说的,但我认为他的文章非常有启发性。

Peter甚至指出,其中一些方案存在性能考虑。考虑到新的ECMAScript Harmony计划已经放弃了4.0版的命名空间和打包,我认为这个话题将是有趣的讨论。


6

我尝试遵循Yahoo的惯例,将一个单一的父对象放在全局范围内来包含所有内容;

(该段内容可能涉及JavaScript编程中的全局变量命名规则)

var FP = {};
FP.module = {};
FP.module.property = 'foo';

5
为了确保您不会覆盖现有对象,您应该像这样做:
if(!window.NameSpace) {
    NameSpace = {};
}

或者

var NameSpace = window.NameSpace || {};

这样,您可以将此放置在应用程序/网站中每个文件的顶部,而无需担心覆盖命名空间对象。此外,这将使您能够为每个文件单独编写单元测试。

3

YUI库包含处理命名空间的代码,其中使用了一个函数,您可能会发现它更加方便。其他库也可能会这样做。


1

我也喜欢这个 (source) :

(function() {
    var a = 'Invisible outside of anonymous function';
    function invisibleOutside() {
    }

    function visibleOutside() {
    }
    window.visibleOutside = visibleOutside;

    var html = '--INSIDE Anonymous--';
    html += '<br/> typeof invisibleOutside: ' + typeof invisibleOutside;
    html += '<br/> typeof visibleOutside: ' + typeof visibleOutside;
    contentDiv.innerHTML = html + '<br/><br/>';
})();

var html = '--OUTSIDE Anonymous--';
html += '<br/> typeof invisibleOutside: ' + typeof invisibleOutside;
html += '<br/> typeof visibleOutside: ' + typeof visibleOutside;
contentDiv.innerHTML += html + '<br/>';​

1
作为点或下划线的替代,您可以使用美元符号字符:
var namespaces$com$example = "data"; 

这提供了什么好处? - eyelidlessness
通过这种方式,您不必将命名空间定义为具有嵌套内部对象的对象。您可以在任何地方定义名称。 - Mark Cidade
жҲ‘д»Қ然зңӢдёҚеҮәдҪҝз”Ё$з¬ҰеҸ·иҖҢдёҚжҳҜзӮ№жҲ–дёӢеҲ’зәҝзҡ„еҘҪеӨ„гҖӮжҲ‘и®Өдёә$з¬ҰеҸ·дҪҝеҚ•иҜҚжҜ”зӮ№жҲ–дёӢеҲ’зәҝжӣҙйҡҫиҜ»гҖӮ - rodrigo-silveira
我认为这比使用嵌套对象要糟糕得多 - 你有大量的单独变量,无论是全局还是局部的,当你查看当前定义的变量时,调试将会变成一场噩梦。 - ThiefMaster

0
使用对象字面量和 this 对象或显式名称来基于包含函数的局部变量的同级属性进行命名空间。例如:

var foo = { bar: function(){return this.name; }, name: "rodimus" }
var baz = { bar: function(){return this.name; }, name: "optimus" }

console.log(foo.bar());
console.log(baz.bar());

或者不使用显式的 name 属性:

var foo = { bar: function rodimus(){return this; } }
var baz = { bar: function optimus(){return this; } }

console.log(foo.bar.name);
console.log(baz.bar.name);

或者不使用this

var foo = { bar: function rodimus(){return rodimus; } }
var baz = { bar: function optimus(){return optimus; } }

console.log(foo.bar.name);
console.log(baz.bar.name);

使用RegExpObject构造函数来为计数变量和其他常见名称添加名称属性,然后使用hasOwnProperty测试进行检查:

 var foo = RegExp(/bar/);
 
/* Add property */
foo.name = "alpha";

document.body.innerHTML = String("<pre>" + ["name", "value", "namespace"] + "</pre>").replace(/,/g, "&#09;");

/* Check type */
if (foo.hasOwnProperty("name")) 
  {
  document.body.innerHTML += String("<pre>" + ["foo", String(foo.exec(foo)), foo.name] + "</pre>").replace(/,/g, "&#09;");
  }

/* Fallback to atomic value */
else 
  {
  foo = "baz";
  }

var counter = Object(1);

/* Add property */
counter.name = "beta";

if (counter.hasOwnProperty("name")) 
  {
  document.body.innerHTML += String("<pre>" + ["counter", Number(counter), counter.name] + "</pre>").replace(/,/g, "&#09;");
  } 
else 
  {
  /* Fallback to atomic value */
  counter = 0;
  }

DOM使用以下约定来命名HTML和SVG元素接口定义的命名空间:

  • HTMLTitleElement
  • SVGTitleElement
  • SVGScriptElement
  • HTMLScriptElement

JavaScript核心使用原型来命名toString方法作为一种简单的多态形式。

参考资料


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