您当前的位置:首页 > 网站建设笔记 >

javascript 为重用命名空间而进行规划

来源:JavaScript DOM 高级程序设计0

为重用命名空间而进行规划

有没有朋友和你同名?如果有,那么你应该很快就能认识到命名空间在脚本中的价值。可以想像一下这个情景:当你要问某人一个问题时,你只叫出了一个名字,结果有好几个人回答,或者根本就没人回答。在你的代码中,要避免这种情况可以使用命名空间——这个JavaScript中最被人忽视但却容易实现的特性之一。不过,这里我要限定一下前面的说法:至少在JavaScript 2.0被广泛使用之前,我所说的都不是真正的命名空间,只是能在脚本内部营造一个属于你自己的小空间的小技巧而已。

正如后面将要学习到的,JavaScript不会抗议多次声明同一个函数,它只是使用最后声明的版本。当你要使用几个自行其事的库时,必须确保它们不会与你自己编写的代码发生冲突。而要避开这些问题困扰,只需记住简单的两点:保持唯一性和不共享。

要保持唯一性,首先要为自己的空间挑选一个不会在别处被使用的名字。例如,Yahoo的库使用的是YAHOO,而Google Maps则在所有标识符中都添加了G前缀。很快你就会看到,我会在本书所有的例子中用ADS来表示Advanced DOM Scripting,不过你也可以将其修改为自己认为更具唯一性的名字。如果你使用自己的名字,那么即使同时加载本书的源代码和你自己的源代码,也可以互不干扰地运行它们。

而“不共享”则是所有魔力的发源地。通过“不共享”(我的意思是什么都不共享),即可实现你的目标。当你在编写脚本时,可能经常会需要一些内部的、专用的函数,这些函数不一定是整个脚本或网站的一部分,但这样反倒会使这些函数的编写及维护更容易。假设你要在自己的网站中创建一个所有人都可以访问的名为alertNodeName()的函数。该函数只用于显示一个HTML元素的nodeName属性。同时,你可能也会创建一个通过id捕获元素的名为$()的函数,以便在alertNodeName()以及你库中的其他函数内部使用它:

<script type="text/javascript">
function $(id) {
    return document.getElementById(id);
}
function alertNodeName(id) {
    alert($(id).nodeName);
}
</script>

还不错,但是由于你的$()函数与来自Prototype库的众所周知的Prototype $()函数并不相同,所以这样一来,其他一些期待不同功能的公共库或脚本可能会因此而停止运行。要保证只有你自己使用这个$()函数,可以使用一些JavaScript技巧。这里所说的技巧指的是一个自执行的匿名函数,相应的代码如下所示:

(function(){
    //代码
})();

可能你以前看到过这样的代码,并且也曾奇怪过它能做什么——应该承认,这是相当优雅的代码。包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后的一对空括号立即执行返回的未命名函数。要是我向其中放入一个参数,那么理解起来大概会更容易一些:

(function(arg){
    alert(arg);
})("This will show in the alert box");

与此相同的伪命名空间的思想,也可以通过稍有不同的对象语法来实现。但它们要完成的任务都是相同的,即保证你的代码被包含在它自己的小空间内。

本质上来看,这也没有做什么特别的,但是,只要你把自己所有的代码都写在这个特殊的函数包装内,那么将没有人能够在这个特殊的包装之外访问到你的任何自定义函数或对象——除非你允许访问。为了避免冲突,你需要像下面这样将自己的$()和alertNodeName()函数放在这个伪命名空间内:

<script type="text/javascript">
(function(){
    function $(id) {
        document.getElementById(id);
    }
    function alertNodeName(id) {
        alert($(id).nodeName);
    }
    window["myNamespace"] = {};
    window["myNamespace"]["showNodeName"] = alertNodeName;
})();
</script>

使用这种伪命名空间可以封装并保护自己的所有函数、对象和变量。而且,由于它们位于同一个函数之中,所以它们之间仍然可以互相访问。不过,脚本其他部分中的代码将无法使用你的函数。现在,你的$()函数以及位于包装中的一切都是你自己的了。

为了对你受保护的部分脚本进行全局化,随后的一对括号告诉浏览器立即执行返回的匿名函数,而且在执行期间,最后几行代码将alertNodeName()赋值给了window的一个方法:

window["myNamespace"]["showNodeName"]

然后,就可以在位于这个命名空间外部的其他脚本中引用这个方法了。为了方便说明这一点,你会注意到我把alertNodeName()重命名为了window方法中的showNodeName()。这样,在包装的外部,将无法直接执行alertNodeName()。但通过将alertNodeName()函数赋值给window["myNamespace"]["showNodeName"],实际上创建了带有下面这样优雅的myNamespace命名空间的通用方法,当执行这个方法时就如同是在执行受保护的alertNodeName()函数一样:

myNamespace.showNodeName("example");

由于存在作用域和闭包,实现前面的内部赋值和外部调用是完全可能的,有关作用域和闭包的内容我们将在本章后面讨论。简言之,当你调用myNamespace.showNodeName()时,这个方法是在你的匿名函数的命名空间中执行的,因此它仍然能够访问你专用的$()函数和该命名空间中的其他对象,以及作用域链中位于这个包装外部的所有对象。

使用匿名函数封装的另外一个好处是,你不需要将自己的命名空间前缀逐一添加到众多的函数和对象上。唯一需要添加命名空间,同时也是你担心可能会因疏忽而覆盖其他标识符的地方,就是给window对象赋值的语句。当然,如果在你的伪命名空间内部覆盖了任何函数(像$),也将会因为明显的异常而引起你对变化的注意,所以这也算不上一个问题。

建站咨询

咨询热线

微信交流

返回顶部