如何在JavaScript中声明命名空间?

时间:2009-05-19 08:11:03

标签: javascript namespaces javascript-namespaces

如何在JavaScript中创建命名空间,以便我的对象和函数不被其他同名对象和函数覆盖?我使用了以下内容:

if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}

有更优雅或简洁的方法吗?

28 个答案:

答案 0 :(得分:1026)

我使用the approach found on the Enterprise jQuery site

以下是他们展示如何申报私人&公共财产和职能。一切都是作为一个自动执行的匿名函数完成的。

(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }
}( window.skillet = window.skillet || {}, jQuery ));

因此,如果您想访问其中一位公开会员,您只需skillet.fry()skillet.ingredients

真正酷的是,现在可以使用完全相同的语法扩展名称空间。

//Adding new Functionality to the skillet
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " +
                     skillet.ingredient + " & " +
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
    };
}( window.skillet = window.skillet || {}, jQuery ));

第三个undefined参数

  

第三个undefined参数是值undefined变量的来源。我不确定它今天是否仍然有用,但在使用旧浏览器/ JavaScript标准(ecmascript 5,javascript< 1.8.5~firefox 4)时,全局范围变量undefined是可写的,所以任何人可以改写它的价值。第三个参数(未传递值时)创建一个名为undefined的变量,该变量的作用域为命名空间/函数。由于在创建名称空间时未传递任何值,因此默认值为undefined

答案 1 :(得分:734)

我喜欢这个:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

答案 2 :(得分:336)

另一种方法,我认为它比对象文字形式的限制更少,是:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

以上内容与the module patternwhether you like it or not非常相似,它允许您将所有函数公开为public,同时避免对象文字的刚性结构。

答案 3 :(得分:156)

  

有更优雅或简洁的方法吗?

是。例如:

var your_namespace = your_namespace || {};

然后你可以

var your_namespace = your_namespace || {};
your_namespace.Foo = {toAlert:'test'};
your_namespace.Bar = function(arg) 
{
    alert(arg);
};
with(your_namespace)
{
   Bar(Foo.toAlert);
}

答案 4 :(得分:90)

我通常在闭包中构建它:

var MYNS = MYNS || {};

MYNS.subns = (function() {

    function privateMethod() {
        // Do private stuff, or build internal.
        return "Message";
    }

    return {
        someProperty: 'prop value',
        publicMethod: function() {
            return privateMethod() + " stuff";
        }
    };
})();

多年来我的风格自写这篇文章以来发生了微妙的变化,现在我发现自己正在写这样的闭包:

var MYNS = MYNS || {};

MYNS.subns = (function() {
    var internalState = "Message";

    var privateMethod = function() {
        // Do private stuff, or build internal.
        return internalState;
    };
    var publicMethod = function() {
        return privateMethod() + " stuff";
    };

    return {
        someProperty: 'prop value',
        publicMethod: publicMethod
    };
})();

通过这种方式,我发现公共API和实现更容易理解。将return语句视为实现的公共接口。

答案 5 :(得分:55)

因为您可以编写不同的JavaScript文件,然后在应用程序中组合或不组合它们,每个文件都需要能够恢复或构造命名空间对象而不会损坏其他文件的工作...

一个文件可能打算使用命名空间namespace.namespace1

namespace = window.namespace || {};
namespace.namespace1 = namespace.namespace1 || {};

namespace.namespace1.doSomeThing = function(){}

另一个文件可能想要使用命名空间namespace.namespace2

namespace = window.namespace || {};
namespace.namespace2 = namespace.namespace2 || {};

namespace.namespace2.doSomeThing = function(){}

这两个文件可以在一起或分开存在而不会发生冲突。

答案 6 :(得分:47)

以下是Stoyan Stefanov在他的 JavaScript Patterns 书中的表现,我发现这本书非常好(它还展示了他如何做出允许自动生成的API文档的注释,以及如何将方法添加到自定义对象的原型中:

/**
* My JavaScript application
*
* @module myapp
*/

/** @namespace Namespace for MYAPP classes and functions. */
var MYAPP = MYAPP || {};

/**
* A maths utility
* @namespace MYAPP
* @class math_stuff
*/
MYAPP.math_stuff = {

    /**
    * Sums two numbers
    *
    * @method sum
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} Sum of the inputs
    */
    sum: function (a, b) {
        return a + b;
    },

    /**
    * Multiplies two numbers
    *
    * @method multi
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} The inputs multiplied
    */
    multi: function (a, b) {
        return a * b;
    }
};

/**
* Constructs Person objects
* @class Person
* @constructor
* @namespace MYAPP
* @param {String} First name
* @param {String} Last name
*/
MYAPP.Person = function (first, last) {

    /**
    * First name of the Person
    * @property first_name
    * @type String
    */
    this.first_name = first;

    /**
    * Last name of the Person
    * @property last_name
    * @type String
    */
    this.last_name = last;
};

/**
* Return Person's full name
*
* @method getName
* @return {String} First name + last name
*/
MYAPP.Person.prototype.getName = function () {
    return this.first_name + ' ' + this.last_name;
};

答案 7 :(得分:32)

我使用这种方法:

var myNamespace = {}
myNamespace._construct = function()
{
    var staticVariable = "This is available to all functions created here"

    function MyClass()
    {
       // Depending on the class, we may build all the classes here
       this.publicMethod = function()
       {
          //Do stuff
       }
    }

    // Alternatively, we may use a prototype.
    MyClass.prototype.altPublicMethod = function()
    {
        //Do stuff
    }

    function privateStuff()
    {
    }

    function publicStuff()
    {
       // Code that may call other public and private functions
    }

    // List of things to place publically
    this.publicStuff = publicStuff
    this.MyClass = MyClass
}
myNamespace._construct()

// The following may or may not be in another file
myNamespace.subName = {}
myNamespace.subName._construct = function()
{
   // Build namespace
}
myNamespace.subName._construct()

外部代码可以是:

var myClass = new myNamespace.MyClass();
var myOtherClass = new myNamepace.subName.SomeOtherClass();
myNamespace.subName.publicOtherStuff(someParameter);

答案 8 :(得分:32)

这是user106826指向Namespace.js的链接的后续内容。该项目似乎已移至GitHub。现在是smith/namespacedotjs

我一直在为我的小项目使用这个简单的JavaScript帮助程序,到目前为止它看起来很轻但功能多,足以处理命名空间加载模块/类。如果它允许我将一个包导入我选择的命名空间,而不仅仅是全局命名空间......叹息,那将是非常好的,但这不仅仅是重点。

它允许您声明命名空间,然后在该命名空间中定义对象/模块:

Namespace('my.awesome.package');
my.awesome.package.WildClass = {};

另一种选择是立即声明命名空间及其内容:

Namespace('my.awesome.package', {
    SuperDuperClass: {
        saveTheDay: function() {
            alert('You are welcome.');
        }
    }
});

有关更多用法示例,请查看the source中的example.js文件。

答案 9 :(得分:29)

样品:

var namespace = {};
namespace.module1 = (function(){

    var self = {};
    self.initialized = false;

    self.init = function(){
        setTimeout(self.onTimeout, 1000)
    };

    self.onTimeout = function(){
        alert('onTimeout')
        self.initialized = true;
    };

    self.init(); /* If it needs to auto-initialize, */
    /* You can also call 'namespace.module1.init();' from outside the module. */
    return self;
})()

您可以选择声明local变量same,如self,并指定local.onTimeout,如果您希望它是私有的。

答案 10 :(得分:13)

您可以声明一个简单的函数来提供名称空间。

function namespace(namespace) {
    var object = this, tokens = namespace.split("."), token;

    while (tokens.length > 0) {
        token = tokens.shift();

        if (typeof object[token] === "undefined") {
            object[token] = {};
        }

        object = object[token];
    }

    return object;
}

// Usage example
namespace("foo.bar").baz = "I'm a value!";

答案 11 :(得分:11)

如果您需要私人范围:

var yourNamespace = (function() {

  //Private property
  var publicScope = {};

  //Private property
  var privateProperty = "aaa"; 

  //Public property
  publicScope.publicProperty = "bbb";

  //Public method
  publicScope.publicMethod = function() {
    this.privateMethod();
  };

  //Private method
  function privateMethod() {
    console.log(this.privateProperty);
  }

  //Return only the public parts
  return publicScope;
}());

yourNamespace.publicMethod();

否则,如果你不使用私人范围:

var yourNamespace = {};

yourNamespace.publicMethod = function() {
    // Do something...
};

yourNamespace.publicMethod2 = function() {
    // Do something...
};

yourNamespace.publicMethod();

答案 12 :(得分:9)

我创建了 namespace ,其灵感来自Erlang的模块。这是一种非常实用的方法,但这就是我最近编写JavaScript代码的方法。

它为闭包提供了一个全局命名空间,并在该闭包中公开了一个已定义的set函数。

(function(){

  namespace("images", previous, next);
  // ^^ This creates or finds a root object, images, and binds the two functions to it.
  // It works even though those functions are not yet defined.

  function previous(){ ... }

  function next(){ ... }

  function find(){ ... } // A private function

})();

答案 13 :(得分:8)

我对命名空间使用以下语法。

var MYNamespace = MYNamespace|| {};

 MYNamespace.MyFirstClass = function (val) {
        this.value = val;
        this.getValue = function(){
                          return this.value;
                       };
    }

var myFirstInstance = new MYNamespace.MyFirstClass(46);
alert(myFirstInstance.getValue());

jsfiddle:http://jsfiddle.net/rpaul/4dngxwb3/1/

答案 14 :(得分:8)

模块模式最初被定义为在传统软件工程中为类提供私有和公共封装的方法。

使用Module模式时,我们可能会发现定义一个用于开始使用它的简单模板很有用。这是一个涵盖名称间距,公共和私有变量。

在JavaScript中,模块模式用于进一步模拟类的概念,使得我们能够在单个对象中包含公共/私有方法和变量,从而将特定部分与全局范围隔离开来。这导致我们的函数名称与页面上其他脚本中定义的其他函数冲突的可能性降低。

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();

<强>优点

为什么Module模式是一个不错的选择?对于初学者来说,对于来自面向对象背景的开发人员来说,这比真正封装的想法更加清晰,至少从JavaScript的角度来看。

其次,它支持私有数据 - 因此,在模块模式中,我们代码的公共部分能够触及私有部分,但外部世界无法触及该类的私有部分。

<强>缺点

Module模式的缺点是,当我们以不同方式访问公共成员和私有成员时,当我们希望更改可见性时,我们实际上必须对使用该成员的每个位置进行更改。

我们也无法在以后添加到对象的方法中访问私有成员。也就是说,在许多情况下,模块模式仍然非常有用,如果使用得当,肯定有可能改善我们的应用程序结构。

揭示模块模式

现在我们对模块模式稍微熟悉一下,让我们看一下稍微改进的版本 - Christian Heilmann的Revealing Module模式。

揭示模块模式的出现是因为Heilmann对于当我们想要从另一个公共方法调用一个公共方法或访问公共变量时必须重复主对象的名称这一事实感到沮丧。他也不喜欢Module模式的要求为了他希望公开的东西,不得不切换到对象字面符号。

他努力的结果是一个更新的模式,我们只需在私有范围内定义所有函数和变量,并返回一个匿名对象,指向我们希望公开的私有功能。

如何使用揭示模块模式的示例可以在下面找到

var myRevealingModule = (function () {

        var privateVar = "Ben Cherry",
            publicVar = "Hey there!";

        function privateFunction() {
            console.log( "Name:" + privateVar );
        }

        function publicSetName( strName ) {
            privateVar = strName;
        }

        function publicGetName() {
            privateFunction();
        }


        // Reveal public pointers to
        // private functions and properties

        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };

    })();

myRevealingModule.setName( "Paul Kinlan" );

<强>优点

此模式允许脚本的语法更加一致。它还使模块结尾处的内容更加清晰,我们可以公开访问哪些函数和变量,这样可以提高可读性。

<强>缺点

此模式的缺点是,如果私有函数引用公共函数,则如果需要补丁,则无法覆盖该公共函数。这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,仅适用于函数。

引用私有变量的公共对象成员也受上面的无补丁规则说明的约束。

答案 15 :(得分:7)

将我的几个库移植到不同的项目后,不得不经常更改顶级(静态命名)命名空间后,我转而使用这个小型(开源)辅助函数来定义命名空间。

global_namespace.Define('startpad.base', function(ns) {
    var Other = ns.Import('startpad.other');
    ....
});

好处的描述在我的blog post。您可以抓取source code here

我真正喜欢的一个好处是模块之间在加载顺序方面的隔离。您可以在加载外部模块之前参考它。当代码可用时,您将获得对象引用。

答案 16 :(得分:7)

我参加晚会已经晚了7年,但是在8年前做了相当多的工作:

能够轻松高效地创建多个嵌套命名空间,以保持复杂的Web应用程序的组织和可管理性,同时尊重JavaScript全局命名空间(防止名称空间污染),并且不破坏命名空间路径中的任何现有对象,这一点非常重要。这样做。

从上面来看,这是我的大约2008年解决方案:

var namespace = function(name, separator, container){
  var ns = name.split(separator || '.'),
    o = container || window,
    i,
    len;
  for(i = 0, len = ns.length; i < len; i++){
    o = o[ns[i]] = o[ns[i]] || {};
  }
  return o;
};

这不是创建命名空间,而是提供创建命名空间的功能。

这可以浓缩为缩小的单行:

var namespace=function(c,f,b){var e=c.split(f||"."),g=b||window,d,a;for(d=0,a=e.length;d<a;d++){g=g[e[d]]=g[e[d]]||{}}return g};

使用示例:

namespace("com.example.namespace");
com.example.namespace.test = function(){
  alert("In namespaced function.");
};

或者,作为一个声明:

namespace("com.example.namespace").test = function(){
  alert("In namespaced function.");
};

然后执行任何一个:

com.example.namespace.test();

如果您不需要对旧版浏览器的支持,请使用更新版本:

const namespace = function(name, separator, container){
    var o = container || window;
    name.split(separator || '.').forEach(function(x){
        o = o[x] = o[x] || {};
    });
    return o;
};

现在,我对将namespace暴露给全局命名空间本身持谨慎态度。 (太糟糕了,基本语言不能为我们提供这个!)所以我通常在闭包中使用它,例如:

(function(){
	const namespace = function(name, separator, container){
		var o = container || window;
		name.split(separator || '.').forEach(function(x){
			o = o[x] = o[x] || {};
		});
		return o;
	};
	const ns = namespace("com.ziesemer.myApp");
	
	// Optional:
	ns.namespace = ns;
	
	// Further extend, work with ns from here...
}());

console.log("\"com\":", com);

在较大的应用程序中,只需在页面加载开始时定义一次(对于基于客户端的Web应用程序)。如果保留了附加文件,则可以重用命名空间功能(在上面包含为“可选”)。在最坏的情况下,如果重新声明此函数几次 - 它只是几行代码,如果缩小则更少。

答案 17 :(得分:5)

您必须检查 Namespace.js

答案 18 :(得分:2)

我喜欢Jaco Pretorius的解决方案,但我希望通过将“this”关键字指向模块/命名空间对象来使其更有用。 我的煎锅版本:

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);

答案 19 :(得分:2)

我最喜欢的模式最近成了这个:

var namespace = (function() {
  
  // expose to public
  return {
    a: internalA,
    c: internalC
  }

  // all private
  
  /**
   * Full JSDoc
   */
  function internalA() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalB() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalC() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalD() {
    // ...
  }
  
})();

当然,返回可以在最后,但如果只有函数声明跟随它,则更容易看到命名空间的全部内容,以及暴露的API。

在这种情况下使用函数表达式的模式导致无法在不遍历整个代码的情况下知道暴露了哪些方法。

答案 20 :(得分:1)

我想你们所有人都为这样一个简单的问题使用了太多的代码。 无需为此进行回购。 这是单行功能。

namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

尝试一下:

// --- definition ---
const namespace = namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

// --- Use ----
let myNamespace = namespace("a.b.c");
myNamespace.MyClass = class MyClass {};

// --- see ----
console.log("a : ", a);

答案 21 :(得分:1)

跟随IonuţG。Stan的回答,但通过使用var ClassFirst = this.ClassFirst = function() {...}展示了整洁代码的好处,var Namespace = new function() { var ClassFirst = this.ClassFirst = function() { this.abc = 123; } var ClassSecond = this.ClassSecond = function() { console.log("Cluttered way to access another class in namespace: ", new Namespace.ClassFirst().abc); console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc); } } var Namespace2 = new function() { var ClassFirst = this.ClassFirst = function() { this.abc = 666; } var ClassSecond = this.ClassSecond = function() { console.log("Cluttered way to access another class in namespace: ", new Namespace2.ClassFirst().abc); console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc); } } new Namespace.ClassSecond() new Namespace2.ClassSecond() 利用了JavaScript的闭包范围,减少了同一名称空间中类的名称空间混乱。

Cluttered way to access another class in namespace: 123
Nicer way to access a class in same namespace: 123
Cluttered way to access another class in namespace: 666
Nicer way to access a class in same namespace: 666

输出:

data TVShow = BobsBurgers | MrRobot | BatmanTAS

答案 22 :(得分:1)

我们可以这样独立使用它:

var A = A|| {};
A.B = {};

A.B = {
    itemOne: null,
    itemTwo: null,
};

A.B.itemOne = function () {
    //..
}

A.B.itemTwo = function () {
    //..
}

答案 23 :(得分:0)

JavaScript默认不支持名称空间。因此,如果您创建任何元素(函数,方法,对象,变量),那么它将变为全局元素并污染全局名称空间。让我们以定义两个不带任何名称空间的函数为例,

function func1() {
    console.log("This is a first definition");

}
function func1() {
    console.log("This is a second definition");
}
func1(); // This is a second definition

它总是调用第二个函数定义。在这种情况下,名称空间将解决名称冲突问题。

答案 24 :(得分:0)

如果使用Makefile,你可以这样做。

// prelude.hjs
billy = new (
    function moduleWrapper () {
    const exports = this;

// postlude.hjs
return exports;
})();

// someinternalfile.js
function bob () { console.log('hi'); }
exports.bob = bob;

// clientfile.js
billy.bob();

一旦我达到大约1000行,我更喜欢使用Makefile,因为我可以通过删除makefile中的一行来有效地注释掉大量的代码。它可以很容易地摆弄东西。此外,使用此技术,命名空间仅在前奏中出现一次,因此它很容易更改,您不必在库代码中继续重复它。

使用makefile时在浏览器中进行实时开发的shell脚本:

while (true); do make; sleep 1; done

将此项添加为生成任务“go”,您可以“开始”在编码时更新您的版本。

答案 25 :(得分:0)

在JavaScript中,没有预定义的方法来使用命名空间。在JavaScript中,我们必须创建自己的方法来定义NameSpaces。以下是我们在Oodles技术中遵循的程序。

注册NameSpace 以下是注册名称空间的功能

'.'

要注册名称空间,只需调用上面的函数,并将参数作为名称空间,用registerNS("oodles.HomeUtilities"); registerNS("oodles.GlobalUtilities"); var $OHU = oodles.HomeUtilities; var $OGU = oodles.GlobalUtilities; (点)分隔。 例如 让您的应用程序名称为oodles。您可以通过以下方法

创建命名空间
var oodles = {
    "HomeUtilities": {},
    "GlobalUtilities": {}
};

基本上它会在后端创建你的NameSpaces结构,如下所示:

"oodles.HomeUtilities"

在上面的函数中,您注册了一个名为"oodles.GlobalUtilities"$OHU的命名空间。为了调用这些名称空间,我们创建了一个变量,即var $OGU和var HomeUtilities

这些变量只不过是初始化命名空间的别名。 现在,每当您声明属于$OHU.initialization = function(){ //Your Code Here }; 的函数时,您将声明如下:

$OHU

上面是函数名称初始化,它被放入名称空间$OHU.initialization(); 。并在脚本文件中的任何位置调用此函数。只需使用以下代码即可。

{{1}}

同样,使用另一个NameSpaces。

希望它有所帮助。

答案 26 :(得分:0)

我的习惯是使用 function myName()作为属性存储,然后使用 var myName 作为&#34;方法&#34;保持器...

这是否合法,打败了我!我一直依赖于我的PHP逻辑,而且事情很简单。 :d

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
 (myObj instanceof Function !== false)
 ? Object.create({

     $props: new myObj(),
     fName1: function() { /* code..  */ },
     fName2: function() { /* code ...*/ }
 })
 : console.log('Object creation failed!')
);

if (this !== that) myObj.fName1(); else myObj.fName2();

  

你也可以用反之亦然的方式来做。在创建对象之前检查的方法好多了

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
    (typeof(myObj) !== "function" || myObj instanceof Function === false)
    ? new Boolean()
    : Object.create({
        $props: new myObj(),
        init: function () { return; },
        fName1: function() { /* code..  */ },
        fName2: function() { /* code ...*/ }
    })
);

if (myObj instanceof Boolean) {
    Object.freeze(myObj);
    console.log('myObj failed!');
    debugger;
}
else
    myObj.init();

参考: JavaScript: Creating Object with Object.create()

答案 27 :(得分:0)

我已经编写了另一个命名空间库,它的工作方式与其他语言的包/单元类似。它允许您创建一个JavaScript代码包和来自其他代码的包的引用:

文件hello.js

Package("hello", [], function() {
  function greeting() {
    alert("Hello World!");
  }
  // Expose function greeting to other packages
  Export("greeting", greeting);
});

File Example.js

Package("example", ["hello"], function(greeting) {
  // Greeting is available here
  greeting();  // Alerts: "Hello World!"
});

页面中只需要包含第二个文件。它的依赖项(本例中的文件 hello.js )将自动加载,从这些依赖项导出的对象将用于填充回调函数的参数。

您可以在 Packages JS 中找到相关项目。