摘要:前缀命名空间如果命名空间的目的是避免冲突的话。语言程序经常使用前缀命名空间。我认为前缀命名空间是中最清楚明白的命名空间系统。对象命名空间的一个问题是它会导致与面向对象消息传递混淆。嵌套命名空间的幻觉在中也存在。
原文链接:《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::Blue 和 Red 或者 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 对象上。像 这样命名是有意义的。
命名空间类名属性,比如 文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。 转载请注明本文地址:https://www.ucloud.cn/yun/79812.html
摘要:原文链接译文原链译中的命名空间全局变量应该由有系统范围相关性的对象们保留,并且它们的命名应该避免含糊并尽量减少命名冲突的风险。其他的考虑我希望避免命名空间嵌套。因为并没有正式的命名空间结构,所以有很多自然形成的方法。 原文链接: Namespacing in JavaScript译文原链: 【译】JavaScript 中的命名空间 全局变量应该由有系统范围相关性的对象们保留,并且它们的...
摘要:原文链接原文作者你想知道的关于作用域的一切译中有许多章节是关于的但是对于初学者来说甚至是一些有经验的开发者这些有关作用域的章节既不直接也不容易理解这篇文章的目的就是为了帮助那些想更深一步学习了解作用域的开发者尤其是当他们听到一些关于作用域的 原文链接: Everything you wanted to know about JavaScript scope原文作者: Todd Mott...
摘要:比如,我们可以监听事件由实例发出,然后在任何浏览器中就是变化的时候都会得到通知,如下所示每一个作用域对象都会有这个方法,可以用来注册一个作用域事件的侦听器。这个函数所扮演的侦听器在被调用时会有一个对象作为第一个参数。 上一篇:【译】《精通使用AngularJS开发Web App》(二) 下一篇:【译】《精通使用AngularJS开发Web App》(四) 书名:Mastering W...
摘要:通用格式规范缩进一次缩进个空格,不要使用或者混合和空格的缩进。语义化根据使用场景选择正确的元素有时被错误的称为标签。格式规范引号属性值用双引号。风格规范和命名使用有含义的和名称。和单位值为时不用添加单位。 原文 Google HTML/CSS Style Guide 背景 这篇文章定义了 HTML 和 CSS 的格式和代码规范,旨在提高代码质量和协作效率。 通用样式规范 协议 图片,样...
摘要:通用格式规范缩进一次缩进个空格,不要使用或者混合和空格的缩进。语义化根据使用场景选择正确的元素有时被错误的称为标签。格式规范引号属性值用双引号。风格规范和命名使用有含义的和名称。和单位值为时不用添加单位。 原文 Google HTML/CSS Style Guide 背景 这篇文章定义了 HTML 和 CSS 的格式和代码规范,旨在提高代码质量和协作效率。 通用样式规范 协议 图片,样...
阅读 1259·2021-11-23 09:51
阅读 1626·2021-11-16 11:45
阅读 4012·2021-10-09 09:43
阅读 2681·2021-07-22 16:47
阅读 943·2019-08-27 10:55
阅读 3448·2019-08-26 17:40
阅读 3082·2019-08-26 11:39
阅读 3227·2019-08-23 18:39