65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 命名空间

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (9投票s)

2013年2月28日

CPOL

3分钟阅读

viewsIcon

31053

通过函数创建命名空间。

引言

来自 C# 或 Java 背景的开发者都知道命名空间的重要性。它是保持代码组织的主要方式。JavaScript 本身并不支持命名空间,或者说,它真的不支持吗?

对象嵌套

JavaScript 是一种动态语言。你可以动态地创建对象,并且对象可以包含其他对象。这个概念允许你创建一个原型结构,从而模拟命名空间。

让我们看一个哺乳动物、猫和狗的例子

var Animal=function()   {
    //we don't know yet
    this.numberOfLegs=null;
    this.sound=null;
    return this;
}
Animal.prototype.getNumberOfLegs=function() {
    return this.numberOfLegs;
}

Animal.prototype.makeSound=function() {
   alert(this.sound);
}

var Mammal=function()   {
    //all mammals have for legs;
    this.numberOfLegs=4;
    return this;
    }
Mammal.prototype=new Animal();

var Dog=function()  {
    this.sound="Woef";
}
Dog.prototype=new Mammal();

var Cat=function()  {
    this.sound="Miauw";
    return this;
}
Cat.prototype=new Mammal();

var Tiger=function()    {
    this.sound="Wroaar";
    return this;
}
Tiger.prototype=new Mammal();

var Leopard=function()    {
    this.sound="Grrrr";
    return this;
}
Leopard.prototype=new Mammal();

这很好。我们可以创建所有的哺乳动物,还可以为鸟类等创建基类... 但是,如果我们还需要维护一个 iOS 名称列表呢?要列出苹果的 iOS 版本,我们需要 Tiger 和 Leopard。

此外,我们应该将 Animal 视为不应直接使用的类。我倾向于将这类类称为 Base。但是,如果没有命名空间,我只能有一个 Base 类。

总而言之,我们希望将我们的动物放在 Animals 命名空间中。允许有一个 iOS 命名空间,其中也包含 Tiger 和 Leopard,以及一个 Base 类。我们欺骗 JS 模拟命名空间的方法是创建嵌套对象,并在其中放置其他对象。

//Create the Animals namespace

if(typeof Animals=='undefined')  {
    window["Animals"]={};   
}

Animals.Base=function()   {
    //we don't know yet
    this.numberOfLegs=null;
    this.sound=null;
    return this;
}
Animals.Base.prototype.getNumberOfLegs=function() {
    return this.numberOfLegs;
}

Animals.Base.prototype.makeSound=function() {
   alert(this.sound);
}

Animals.Mammal=function()   {
    //all mammals have for legs;
    this.numberOfLegs=4;
    return this;
    }
Animals.Mammal.prototype=new Animals.Base();

Animals.Dog=function()  {
    this.sound="Woef";
    return this;
}
Animals.Dog.prototype=new Mammal();

//...etc

注意省略了 'var'。我们所做的是将所有内容添加到全局命名空间中。有些人认为这是绝对禁止的,但 jQuery、ExtJs、YUI 和 prototype 都是这样做的。事实是,如果你将所有类甚至函数放在一个命名空间中,比如你的产品名称,那么实际上可以防止污染全局命名空间。

除此之外,我倾向于将所有代码包装在一个匿名函数内的 .js 文件中。这样,你就可以拥有只在该源文件中可用的“全局”变量,并且它们不会污染全局命名空间。

(function() {
    //Create OurProduct namespace
    
    if(typeof OurProduct=='undefined')  {
        window["OurProduct"]={};   
    }
    
    var someVariableAvailableToAllClassesWithinThisFunctionButNotOutside="Hello";
    
    //Create the Animals namespace inside the OurProduct namespace
    
    if(typeof OurProduct.Animals=='undefined')  {
        window["OurProduct"]["Animals"]={};   
    }
    
    OurProduct.Animals.Base=function()   {
        //we don't know yet
        this.numberOfLegs=null;
        this.sound=null;
        return this;
    }
    .......
})();
//Note the () at the end. If you omit that this code will never be run.

注册命名空间

以这种方式进行命名空间实际上只是创建嵌套对象。由于 window 本身就是一个对象,因此你可以直接在其中创建对象。但是,如果我们需要在一个不存在的嵌套命名空间中创建一个对象,那么我们需要先创建它。

MyProduct.Objects.Person=function() {
    //This will fail, because MyProduct.Objects doesn't yet exist.
}

我们希望有一个像 NameSpace.register("MyProduct.Object") 这样的函数。所以,让我们创建它。

(function() {
    Namespace = {
        register : function(ns) {
            //Split the string
            var nsParts=ns.split(".");
            //Store window in obj
            var obj=window;
            //Travese through the string parts
            for(var i=0, j=nsParts.length; i<j; i++)    {
                
                if(typeof obj[nsParts[i]] == "undefined") {
                    //Is it did not exist create it and copy it to obj
                    //so the next part can be copied into this part.
                    obj=obj[nsParts[i]]={};
                }
                else    {
                    //Still copy it to obj, cause the next one might not exist
                    obj=obj[nsParts[i]];
                }
            }
        }
    }

    NameSpace.register("MyProduct.Objects");
    MyProduct.Objects.Person=function() {
        alert("yeee this is legal")
    }   
    var P=new MyProduct.Objects.Person();
})();

这正在取得进展。这个函数已经在我的项目中使用了多年。直到我开始思考,“既然我总是将类包装在匿名函数中,为什么不让这个函数更有用呢?” 此外,我的 C# 代码看起来像这样

namespace MyProduct.Objects {
    
    public class Person {
        ....
    }
}

当然,我们不能也不应该试图让代码看起来完全一样,但我们可以拥有一些非常相似的东西。

Namespace("MyProject.Objects", function()  {
    
    MyProduct.Objects.Person=function() {
        ....    
    }
});
//notice the omission of the ()
//starting the function will be a task of the namespace function

那么我们该如何实现呢?让我们创建命名空间函数

//This function itself is'nt Namespaced so it should be wrapped in an anonymous function
(function() {
    //no var so it will end up in the global namespace
    Namespace=function(ns, fs)   {
        //ns Namespace as . seperated string
        //fs the return function
        
        //Here's our trusty register function, only now it's inline 
        var register = function(ns) {
            //Split the string
            var nsParts=ns.split(".");
            //Store window in obj
            var obj=window;
            //Travese through the string parts
            for(var i=0, j=nsParts.length; i<j; i++)    {
                
                if(typeof obj[nsParts[i]] == "undefined") {
                    //Is it did not exist create it and copy it to obj
                    //so the next part can be copied into this part.
                    obj=obj[nsParts[i]]={};
                }
                else    {
                    //Still copy it to obj, 'cause the next one might not exist
                    obj=obj[nsParts[i]];
                }
            }
        }      
        //Let's register the namespace
        register(ns);
        
        //And call the wrapper function. 
        fs();     
    };
})();

我使用大写字母作为命名空间的首字母。通常我会用小写字母开始一个函数。但是,例如,class 这个词是一个保留字。所以我担心 namespace 可能会在未来成为一个保留字。

结论

许多人认为 JavaScript 不是一种“真正的”语言。但无论你的观点如何,这都不是随意编写长函数链和页面代码中 if-then-else 语句的理由。一旦你遇到任何可能被认为具有状态并且可能随时间变化的东西,你就需要开始考虑为它创建对象。一旦你这样做,你就需要一个命名空间来放置它们。起初这可能看起来很荒谬,但随着你的代码库越来越大,它会更有意义。

© . All rights reserved.