【译】JavaScript 定名空间

原文链接:《JavaScript Namespacing》
译文原链:【译】JavaScript 定名空间

JavaScript 中有许多能够给你的对象平安分派定名空间的要领。这篇文章议论我见过的广泛的实践。

前缀定名空间

假如定名空间的目标是防止争执的话。下面这个体系,只需我们晓得全局变量名前缀 myApp_ 是唯一的,能够像其他体系一样防止定名空间争执。

// add uniquely named global properties
var myApp_sayHello = function() {
  alert('hello');
};
var myApp_sayGoodbye = function() {
  alert('goodbye');
};

// use the namespace properties
myApp_sayHello();

C 言语顺序常常运用前缀定名空间。在 JavaScript 的天下中,你能够会遇见 Macromedia 的 MM_ 要领,比方 MM_showHideLayers。

我以为前缀定名空间是 JavaScript 中最清晰邃晓的定名空间体系。(下面的对象定名空间战略在加入了 this 关键字后会致使疑心。)

前缀定名空间确实建立了许多全局对象。这关于前缀用来防止的定名空间争执并非什么题目。前缀定名空间的题目是,有些网页浏览器(比方 IE6)在有许多全局对象时表现很蹩脚,就我所据说。我做了一些测试而且发现有一个 comp.lang.javascript 的小线程,不过我没有就这个话题研讨完整。

单对象定名空间

当下,最盛行的 JavaScript 定名空间实践是运用一个全局变量来援用一个对象。这个被援用的对象援用你的『真正的营业』,而且由于你的全局对象的定名举世无双,你的代码和其他人的代码就能够一同嗨皮地运转。

假如你一定这个天下上没有任何人用了这个全局变量名 myApp,那末你能够有如许的代码:

// define the namespace object
var myApp = {};

// add properties to the namespace object
myApp.sayHello = function() {
  alert('hello');
};
myApp.sayGoodbye = function() {
  alert('goodbye');
};

// use the namespace properties
myApp.sayHello();

当上面代码的末了一行执行时,JavaScript 诠释器起首找到 myApp 对象,然后找到并挪用这个对象的 syaHello 属性。

对象定名空间的一个题目是它会致使与面向对象音讯通报殽杂。这两者之间并没有显著的句法差别:

// 1
namespace.prop();

// 2
receiver.message();

更细致地研讨这个殽杂,我们得出下面的定名空间主张。假定我们有以下库。

var myApp = {};

myApp.message = 'hello';

myApp.sayHello = function() {
  alert(myApp.message);
};

用这个库的代码能够随便举行写操纵。

myApp.sayHello(); // works
  
var importedfn = myApp.sayHello;

importedfn(); // works

将这个和谁人使人殽杂的运用 this 的音讯通报版本比较一下。

var myApp = {};

myApp.message = 'hello';

myApp.sayHello = function() {
  alert(this.message);
};

用这个库的代码能够随便举行写操纵。

myApp.sayHello() // works because "this" refers to myApp object.

var importedfn = myApp.sayHello;

importedfn(); // error because "this" refers to global object.

这里面的要上的一课是,this 永久不能援用一个被作为定名空间的对象由于它肯能致使关于从定名空间引入标识符的殽杂。这个题目是 this 在我的 JavaScript Warning Words 列表中的缘由之一。

(这也表清楚明了库的 API 属性应当指向用一个要领,如许这些要领能够被导入其他定名空间。这个题目是在我的文章 Lazy Function Definition Pattern 的批评中被指出的。懒散要领定义能够在被隐蔽在库中而且不是 API 的部份时平安运用。)

嵌套对象定名空间

嵌套对象定名空间是另一个广泛的实践,它扩大了对象定名空间的主张。你能够见过相似以下代码:

YAHOO.util.Event.addListener(/*...*/)

处理上面的代码须要诠释器起首找到全聚德 YAHOO 对象,然后它的 util 对象,然后它的 Event 对象,然后找到并挪用它的 addListener 属性。如许的话每次事宜处理器绑定到一个 DOM 元素上花的工夫太多了,因而导入的观点最先被采纳。

(function() {
  var yue = YAHOO.util.Event;
  yue.addListener(/*...*/);
  yue.addListener(/*...*/);
})();

假如你清晰 YAHOO.util.Event.addListener 要领不会用 this 关键字而且永久援用同一个要领,那末导入能够变得越发简约。

(function() {
  var yuea = YAHOO.util.Event.addEventListener;
  yuea(/*...*/);
  yuea(/*...*/);
})();

我觉妥当目标只是防止标识符争执时,嵌套对象定名空间的庞杂是不必要的。岂非 Yahoo! 还以为这些全局标识符 YAHOO_util_Event 和 YAHOO_util_Event_addEventListener 不够奇特吗?

我以为运用嵌套对象定名空间的效果是要看起来和 Java 包定名传统一样,这在 Java 中开支不大。比方,在 Java 中你能够看到以下:

package mytools.text;

class TextComponent {
  /* ... */
}

一个这个类的完整及格的援用应当是 mytools.text.TextComponent

下面是 Niemeyer 和 Knudsen (写)的 Learning Java 中包定名的形貌:

包名是按层级构成的,运用点分开的定名传统。包名构成成分给编译器和运转体系构成了举世无双的定位文件的途径。然则,它们并没在包之间建立其他的关联。并没有什么『subpackage』的说法,事实上,包定名空间是直接的,而非层级的。在包层级关联特定部份的包仅仅是由于习气而有关联。比方,假如我们穿件了另一个叫做 mytools.text.poetry 的包(假定是为了跟诗有关的一些笔墨类),这些类并非 mytools.text 包的一部份;它们没有包成员的接见权限。

嵌套定名空间的幻觉在 Perl 中也存在。在 Perl 中,嵌套包名由双冒号分开开。你能够看到以下 Perl 代码:

package Red::Blue;
our $var = 'foo';

一个完整及格的上述变量援用应当是 $Red::Blue::var

在 Perl 中,就像 Java,定名空间层级的主张只是轻易顺序员,而不是言语自身请求。Wall,Christiansen 和 Orwant 的 Programming Perl 诠释道:

双冒号可被用于链接在包名 $Red::Blue::var 中标识符。这意味着 $var 属于包 Red::Blue。包 Red::Blue 跟能够存在的 Red 包或 Blue 包一点关联都没有。只是说,Red::BlueRed 或许 Blue 之间的关联能够关于写代码或许运用这个顺序的人有什么意义,但跟 Perl 没紧要。(好吧,除了在如今的完成中,符号表 Red::Blue 恰好存在符号表 Red 中。然则 Perl 言语并没有直接利用过它。)

上述援用中末了备注暗示了 Perl 能够有和在 JavaScript 中运用嵌套定名空间对象一样的标识符争执开支。假如 Perl 的完成改变了,这个开支就会消逝。在 JavaScript 中,我一定嵌套对象定名空间的开支永久不会消逝由于 JavaScript 运用耽误绑定。

我并不以为 JavaScript 中的嵌套对象定名空间供应了任何大优点,不过假如不运用导入的话在运转时能够会开支非常大。

一个折衷计划

假如纯真地前缀定名空间在某些浏览器中真的很慢,而嵌套定名空间的观点协助在开发者脑中坚持各事件的有序,那我以为上述 Yahoo! 的例子也能够如许写:

YAHOO.util_Event_addListener

或许用更多的全局称号:

YAHOO_util_Event.addListener

哪一个维度的定名空间?

Perl 的 CPAN 模块是基于他们所做的事变举行定名空间治理的。比方,我写了一个这个定名空间里的模块:

JavaScript::Minifier

假如他人用一样的名字写他本身的模块,而且他不自知地经由过程某些模块依靠经由过程同一个名字运用 CPAN 模块,那末就会有争执。

Java 顺序员采纳最冗杂但固然也是最平安的要领。(Java 顺序员好像都想着在大型体系上运转的代码。)在 Java 中,包常常是基于谁写的做什么的来定名。(myFunc作风的规范化。)『谁写的』部份以至运用开发者本身的相对能够保证唯一性的名字。假如我写一个 Java 的 minifier,由于我有 michaux.ca 的域名,我能够用以下定名空间:

ca.michaux.javascript.minifier

在 JavaScript 中,经由此次议论,能够如许写效力更高:

ca_michaux_javascript_minifier

由于 JavaScript 是以文本的情势效劳的,如许的定名空间能够开支太大,由于增加了下载时刻。Gzip 紧缩会找到大众的字符串并用短字符串替代它们。假如 gzip 不可用的话那末就能够斟酌运用导入了。

var ca_michaux_javascript_minifier = {};

(function() {
  var cmjm = ca_michaux_javascript_minifier;
  
  // refer to cmjm inside this module pattern
})();

我并非说这些长的定名空间是相对必需的,不过他们一定是防止定名空间争执的最平安要领。

其他定名空间题目

标识符不仅在 JavaScript 资本中建立。一个表单的 name 属性也被加在 document.forms 对象上。像 <form name=”myCompany_login”> 如许定名是有意义的。

定名空间类名属性,比方 <div class=”myCompany_story”>,能够在削减 CSS 定名空间争执以及当 JavaScript 代码在经由过程类名搜刮 DOM 元素时很有代价。

总结

我个人以为,任何像 YAHOO.util.Event.addListener 如许有点或许下划线的东西都是被争执吓傻了的。它能够就是 YUI.on。Dojo 经由过程一样功用的 dojo.connect 供应了充足的庇护,由于它有效地涵盖了定名空间『谁』和『做什么』的维度。没有人会在他们的右脑中会如许想并在 dojo 定名空间下写一个 JavaScript 库。Dojo 的开发人员也不会遗忘他们已经有了一个 connect 要领并写别的一个。

假如我们能有一个网站来让顺序员们保留他们的 JavaScript 全局标识符和下划线前缀,而且当 ECMAScipt4 宣布了的时刻也包含他们的包名,就好了:『JavaScript 定名空间登记处』。

JavaScript 是一个最小观点集的壮大言语。纵然 JavaScript 并没有特地为防止定名空间争执设想的言语级支撑,照样有许多处理题目的要领。并没有一个『准确』的答案。选一个你最喜欢的。

不过,不管你做什么,请记着别弄一个别的的全局 $ 标识符。

更多

comp.lang.javascript discussion on namespacing

    原文作者:CHENGKANG
    原文地址: https://segmentfault.com/a/1190000005820534
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞