dinghao

记录成长点滴

 

Ajax-JavaScript-namespace

引入Namespace的目的

JavaScript中引入NamespaceC#一样,都是为了避免命名冲突。但是Js又有些特殊:

js解释执行,如果后面定义了同名的变量会覆盖前面变量,并用应用新定义变量的语义。这在Js中合法,没有错误提示,因此bug很难找。

可以想象,我调用了一个第三方模块,如果这个模块没有定义在Namespace中,我定义的变量和函数很可能就覆盖了第三方的同名符号。

模块原则

a module should never add more than a single symbol to the global namespace

只有这样才能保证模块的重用性,MicrosoftAJax就是这样实现的。除了几个以$开头的全局符号,其余符号都定义在Sys命名空间中。

$符号

MA.js,部分违反了上面的原则,但是它对违反原则的变量命名都以$开头。这样也对模块的调用者有个约定,即:$开头的变量属于全局名称空间,调用者如果也以$命名变量必须自觉保证没有冲突。可以看到Animaions.js有类似用法。

类似的约定还有:以_开头的变量为私有,不应该调用。他们的实现可能会变化,调用者如果应用,必须做层封装。

Namespace通常实现

对于JS这种动态语言来说,实现namespace很简单,定义一个对象变量即可。

如:

var Sys={};

Sys.Debug
={trace:'trace',fail:'fail'};

Sys.StringBuilder
={};

document.write(Sys.Debug.fail);

定义了Sys名称空间,在其中定义了DebugStringBuilder对象。

上面的定义过于简单,实际上还要做一下检测,看看是否已经定义过相同的名称空间,如果有跑出异常或者用已经存在的名称空间;是否namespace是对象,不是也抛异常。

 

var Sys= undefined;

if(!Sys) Sys={};

else if(typeof Sys!=object)

throw new Error('类型错误');

Sys.Debug
={trace:'trace',fail:'fail'};

Sys.StringBuilder
={};

document.write(Sys.Debug.fail)

上面是比较严格的实现,MicrosoftAjax.js,就是用上面的逻辑,只是他代码更复杂些。

注:在Antechinus中如果不指定undefined,会出错。但是Js语法没有这种要求。猜测Antechinus可能会先读取Sys,读没有声明的变量会有异常。

MicrosoftAjax.js实现

MA中注册Namespace,通过循环的方式,window对象保持对rootNamespace的引用,rootNamespace又引用子空间。形成一条链。

如注册:Type.registerNamespace('My.I.M');

会注册下面三个空间:MyMy.IMy.I.M。其中MyrootNamespaceMyMy.I被隐含注册,可以直接使用。

MA的实现看起来很复杂,但是只要注意下面几条还是很容易看懂的:

利用window

var rootObject = window;

在第一次循环中rootObject一直指向window,如果根没有被注册则在window中注册根命名空间。结果是:window[‘My’]={},window===rootObject

第二次循环,winodw指向rootObject[‘My’]。以My为根,注册I。结果:window[‘My’][‘I’]={}window===rootObject[‘My’]

第三次注册My.I.M

因为用到了windowRegisterNamespace也失去了通用性。

Eval

值支持primitive value

eval(code)

code

A string that contains the JavaScript expression to be evaluated or the statements to be executed.

Returns

The value of the evaluated code, if any.

类似数组的语法访问对象

winodw[‘Sys’][‘Net’]

代码:

Type.registerNamespace = function Type$registerNamespace(namespacePath) {

    
/// <param name="namespacePath" type="String"></param>

    
var e = Function._validateParams(arguments, [

        
{name: "namespacePath", type: String}

    ]);

    
if (e) throw e;

 

    
if (!Type.__fullyQualifiedIdentifierRegExp.test(namespacePath)) throw Error.argument('namespacePath', Sys.Res.invalidNameSpace);

    
var rootObject = window;

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

 

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

        
var currentPart = namespaceParts[i];

        
var ns = rootObject[currentPart];

        
if (ns && !ns.__namespace) {

            
throw Error.invalidOperation(String.format(Sys.Res.namespaceContainsObject, namespaceParts.splice(0, i + 1).join('.')));

        }


        
if (!ns) {

            ns 
= rootObject[currentPart] = {};

            
if (i === 0{

                window.__rootNamespaces[window.__rootNamespaces.length] 
= ns;

 

            }


                        

            ns.__namespace 
= true;

            ns.__typeName 
= namespaceParts.slice(0, i + 1).join('.');

              

            
var parsedName;

            
try {

                parsedName 
= eval(ns.__typeName);

            }


            
catch(e) {

                parsedName 
= null;

            }


            
if (parsedName !== ns) {

                
delete rootObject[currentPart];

                
throw Error.argument('namespacePath', Sys.Res.invalidNameSpace);

            }


            ns.getName 
= function ns$getName() {return this.__typeName;}

        }


        rootObject 
= ns;

    }


}

posted on 2007-07-18 14:01  思无邪  阅读(4435)  评论(11编辑  收藏  举报

导航