javascript实现命名空间

在小规模的脚本开发中,有时候并不值得去引用命名空间,因为会带来某种程度的复杂性;但是当在同一个网页里引入10多个js文件之后,各js中的同名函数就很容易冲突了。比如xxx库里写了个addCssStyle方法,yyy类库里也写了个addCssStyle方法,而这两个方法的具体实现又有一定差别。那么同时引用这两个组件的时候,函数冲突之后导致页面效果发生变化,调试和修改都是非常痛苦的,如果为了避免冲突,而放弃引用一些优秀的组件,那更是让人郁闷的事情。为此,在封装javascript组件库的时候,请使用命名空间来避免冲突。将所有的方法和变量都要按包名类名的方式来写。(这个时候写代码的感觉和封装java的util方法一样方便,呵呵)

所以,请记住:请为你封装的JavaScript库加上命名空间以提高代码重用性。

但是JavaScript原生并不支持命名空间,需要变通来实现。

在JavaScript中,所有的对象(或者称类型,例如Boolean,Array,Function)都可以认为是一个关联数组。关联数组中的对象可以使用点(.)进行引用,这样我们可以利用关联数组变相地实现命名空间。首先声明一个关联数组作为根,因为页面声明的对象都是window这个变量的成员,所以一般命名空间的实现都以window为根。当向window申请a.b.c的命名空间时,首先在window中查看是否存在a这个成员,如果没有则在window下新建一个名为a的空关联数组,如果已经存在a,则继续在window.a中查看b是否存在,以此类推。下面分别是Atlas和YUI中的实现方法。

//Atlas命名空间的实现方法

Function.registerNamespace =function(namespacePath){

//以window为根

var rootObject =window;

//对命名空间路径拆分成数组

var namespaceParts =namespacePath.split('.');

for (var i =0;i <namespaceParts.length;i++) {

   var currentPart =namespaceParts[i];

   //如果当前命名空间下不存在,则新建一个Object对象,等效于一个关联数组。

   if (!rootObject[currentPart])     {

      rootObject[currentPart]=new Object();

   }

   rootObject =rootObject[currentPart];

}

}

Atlas的实现通俗易懂。Javascrip中rootObject[currentPart]=new Object();和rootObject[currentPart]={};是等效的两种写法。

//YUI命名空间的实现方法

var YAHOO = window.YAHOO || {};

YAHOO.namespace = function(ns) {

if (!ns || !ns.length) {

   return null;

   }

   var levels = ns.split(".");

   var nsobj = YAHOO;

   //如果申请的命名空间是在YAHOO下的,则必须忽略它,否则就成了YAHOO.YAHOO了

   for (var i=(levels[0] == "YAHOO") ? 1 : 0; i<levels.length; ++i) {

   //如果当前命名空间下不存在,则新建一个关联数组。

   nsobj[levels[i]] = nsobj[levels[i]] || {};

   nsobj = nsobj[levels[i]];

   }

   //返回所申请命名空间的一个引用;

   return nsobj;

};

YUI的实现带有一点C风格,nsobj[levels[i]] = nsobj[levels[i]] || {};这句相比于Atlas显得要晦涩一些。

比较一下Atlas和YUI的实现,YUI稍微好一些,因为YUI中申请命名空间的时候会返回一个引用,可以赋值给一个变量,这就相当于声明了该名称空间的一个别名,编码会方便不少。YUI把所有申请的命名空间都放在了window.YAHOO下面,这样有什么好处呢?假如Yahoo和其他公司有合作关系,需要嵌入对方的脚本时,这样能保证它自己编写的代码都在YAHOO这个空间下面,而其他公司不大可能在这个空间下面编码,就基本不会出现命名冲突的情况。我觉得这个做法好,是因为在不对等的合作关系中,要求对方去修改代码来适应你的应用是不现实的,YUI的实现考虑了这点。

还有一点,所有的命名空间都放在window.YAHOO下是最好最合理的做法嘛?在大量应用动态效果的页面中,这势必要维护一个超大的关联数组。关联数组本质是哈希数组,检索数组成员的开销可以忽略不计,但是window.YAHOO承载的东西太多太复杂,这是否违背了大道至简的原则?有没有更好的办法?

例子:

//1、命名空间注册工具类
var Namespace = new Object();

Namespace.register = function(path) {
var arr = path.split(".");
var ns = "";
for ( var i = 0; i < arr.length; i++) {
   if (i > 0)
    ns += ".";
   ns += arr[i];
   eval("if(typeof(" + ns + ") == 'undefined') " + ns + " = new Object();");
}
}

//2、注册命名空间 com.cjm.ui
Namespace.register("com.cjm.ui");

//3、使用命名空间
com.cjm.ui.TreeGrid = function() {
this.sayHello = function(name) {
   alert("Hello " + name);
}
}

var t = new com.cjm.ui.TreeGrid();
t.sayHello("uid");

相关推荐