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

How do I declare a namespace in JavaScript?

提问人:Scott McKenzie 提问时间:5/19/2009 最后编辑:George StockerScott McKenzie 更新时间:1/12/2022 访问量:633022

问:

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

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

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

JavaScript 命名空间 javascript 命名空间

评论

21赞 annakata 5/19/2009
我可以看到你要去哪里检查是否占用了命名空间,但由于如果失败,将不会创建对象,我认为更好的方法是在命名空间被占用时发出警报。坦率地说,这在大多数 JS 情况下不应该发生,应该在开发中迅速发现。
19赞 7/4/2010
采用顶级“命名空间”(window 属性)。拥有它。应在测试的早期检测到冲突。不要费心添加所有这些“假设”检查。对于重复的“命名空间”来说,这是一个致命的问题,应该这样对待。您可以遵循 jQuery 之类的方法,以允许驻留在自定义“命名空间”中;但这仍然是一个设计时问题。
0赞 Tim Abell 6/15/2012
另请参阅 stackoverflow.com/questions/2102591/... 了解性能问题
0赞 Tim Abell 6/15/2012
另请参阅 stackoverflow.com/questions/4125479/... 了解对象与函数命名空间
1赞 Sebastian Simon 7/5/2021
嗯,现在我们有符号和模块,所以重复的命名空间甚至不应该成为问题。

答:

814赞 dfa 5/19/2009 #1

我喜欢这个:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

评论

68赞 annakata 5/19/2009
重要的一点是要虔诚地扩展不超过一个根变量。一切都必须由此而来。
24赞 mckoss 3/19/2011
这不会为您的代码创建闭包 - 它使调用其他函数变得乏味,因为它们始终必须看起来像:yourNamespace.bar();我做了一个开源项目就是为了解决这个设计问题:github.com/mckoss/namespace
28赞 user406905 3/31/2011
annakata:“重要的一点是要虔诚地扩大,不要只限于一个根变量。- 为什么会这样?
12赞 Ryan 5/15/2012
@alex - 为什么要有浅层物体结构?
28赞 alex 5/15/2012
@Ryan我的意思是一切都应该在 ,例如 而不是 和 。不一定只有两个层次的深度。MyAppMyApp.Views.Profile = {}MyApp.users = {}MyViews.Profile = {}
33赞 AnthonyWJones 5/19/2009 #2

我使用这种方法:

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);

评论

0赞 John 10/19/2010
很棒的细节!谢谢!只是想知道你对命名空间.js的看法。我自己从未使用过它,所以我想知道有你的知识/技能/经验的人是否会考虑使用它。
0赞 yoosiba 2/16/2011
我喜欢!另一方面,我在这个外部代码的第一行出现异常,说:“myNameSpace.MyClass”[undefined]不是构造函数。也许这取决于JS的实现?:/
0赞 AnthonyWJones 2/16/2011
@yossiba:有可能。上面的代码是相当标准的东西。在标准 JS 中,任何函数都可以用作构造函数,无需执行任何操作即可将函数标记为专门用作构造函数。您是否正在使用不寻常的风格,例如 ActionScript 或其他东西?
0赞 paul 8/5/2011
@Anthony最好使用 var MYNAMESPACE = MYNAMESPACE ||{};仅使用 var myNamespace = {} 是不安全的,而且最好用大写字母声明您的命名空间
10赞 AnthonyWJones 8/5/2011
@paul:“更好”可能是相当主观的。我讨厌阅读对我大喊大叫的代码,所以我避免使用全部大写的标识符。虽然看起来更具防御性,但它可能会导致其他意想不到的结果。ns = ns || {}
357赞 Ionuț G. Stan 5/19/2009 #3

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

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

上面的内容与模块模式非常相似,无论您喜欢与否,它都允许您将所有函数公开为公共函数,同时避免对象文字的僵化结构。

评论

18赞 Ionuț G. Stan 5/19/2009
1. OLN和模块模式是有区别的。2. 我/总是/不喜欢 OLN,因为你必须记住不要加上最后一个尾随逗号,并且你的所有属性都必须用一个值(如 null 或 undefined)初始化。此外,如果需要成员函数的闭包,则需要为每个方法提供小型函数工厂。另一件事是,您必须将所有控制结构都包含在函数中,而上述形式并没有强加于此。这并不是说我不使用OLN,只是有时我不喜欢它。
11赞 Lawrence Barsanti 10/14/2010
我喜欢这种方法,因为它允许私有函数、变量和伪常量(即 var API_KEY = 12345;)。
12赞 Lucent 11/7/2010
我更喜欢这个,而不是投票更高的逗号分隔对象容器。相比之下,我也看不出有什么缺点。我错过了什么吗?
7赞 John Kraft 8/12/2011
JS新手在这里...为什么我不必打字,那就是...... 工程。ns().publicFunction()ns.publicFunction()
15赞 Ionuț G. Stan 8/12/2011
@John Kraft 来说,这是关键字前面的关键字的 ne-cause。基本上,它所做的是声明一个匿名函数(作为一个函数,它也是一个构造函数),然后它立即使用 .因此,存储在其中的最终值是该匿名构造函数的(唯一)实例。希望这是有道理的。newfunctionnewns
8赞 mckoss 3/27/2010 #4

在将我的几个库移植到不同的项目,并且必须不断更改顶级(静态命名)命名空间之后,我已切换到使用这个小型(开源)帮助程序函数来定义命名空间。

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

好处的描述在我的博客文章中。你可以在这里获取源代码

我非常喜欢的一个好处是模块之间在负载顺序方面的隔离。您可以在加载外部模块之前对其进行引用。当代码可用时,您获得的对象引用将被填充。

评论

1赞 mckoss 8/29/2010
我创建了命名空间库的改进版本(2.0):code.google.com/p/pageforest/source/browse/appengine/static/src/...
0赞 snoob dogg 7/24/2019
你所有的链接似乎都死了
160赞 Alex Pacurar 5/26/2010 #5

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

是的。例如:

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);
}

评论

1赞 mjallday 11/5/2010
这让我在 IE7 中出现错误。var your_namespace = (typeof your_namespace == “undefined” || !your_namespace ) ?{} : your_namespace ;效果更好。
9赞 Palo 3/9/2011
它应该是 var your_namespace = your_namespace = your_namespace ||{} 适用于所有浏览器;)
2赞 Sriram 5/29/2013
@Palo 你能解释一下为什么会这样吗?var your_namespace = your_namespace = your_namespace || {}
6赞 Alex Pacurar 7/29/2014
您可以在不同的 JS 文件中扩展 your_namespace 对象。使用 var your_namespace = {} 时,您不能这样做,因为对象将被每个文件覆盖
1赞 aamarks 5/23/2018
然而,MDN 不鼓励使用 ?with
9赞 The Who 7/4/2010 #6

我创建了受 Erlang 模块启发的命名空间。这是一种非常实用的方法,但这就是我现在编写 JavaScript 代码的方式。

它为闭包提供全局命名空间,并在该闭包中公开定义的集合函数。

(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

})();
32赞 Rudy Lattae 8/28/2010 #7

这是 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.');
        }
    }
});

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

评论

2赞 SamStephens 9/19/2011
只要您记住,这会产生一些性能影响,因为每次访问 my.awesome.package.WildClass 时,您都会访问 my 的 awesome 属性、my.awesome 的 package 属性和 my.awesome.package 的 WildClass 属性。
59赞 Fentex 11/9/2010 #8

因为你可能会编写不同的 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(){}

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

评论

1赞 DVK 8/15/2016
我发现这是一种非常有用的方法,可以在功能需要模块化的大型应用程序中将客户端脚本组织成多个文件。
0赞 Ciro Santilli OurBigBook.com 10/18/2016
专门询问多个文件的问题:stackoverflow.com/questions/5150124/...
1赞 Stijn de Witt 2/6/2011 #9

我写了另一个命名空间库,它的工作方式更像其他语言中的包/单元。它允许您创建 JavaScript 代码包以及从其他代码包中引用该代码包:

文件你好 .js

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

文件示例:.js

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

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

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

评论

1赞 Stijn de Witt 12/29/2015
@peter-mortensen 对我 11 年的回答进行这些编辑真的有必要吗?你的所作所为绝对不是故意破坏,不要误会我的意思,但它们非常肤浅。我宁愿保持此类帖子的唯一作者,除非你真的添加了一些好东西。
1赞 Samuel Danielson 4/13/2011 #10

如果使用 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

将其添加为 make 任务 'go',你可以 'make go' 以在编码时保持构建更新。

1073赞 Jaco Pretorius 5/10/2011 #11

我使用在Enterprise jQuery网站上找到的方法

下面是他们的示例,展示了如何声明私有和公共属性和函数。一切都是作为自动执行的匿名函数完成的。

(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

第三,参数是变量 value 的来源。我不确定它今天是否仍然相关,但是在使用较旧的浏览器/JavaScript 标准(ecmascript 5、javascript < 1.8.5 ~ firefox 4)时,global-scope 变量是可写的,因此任何人都可以重写它的值。第三个参数(当未传递值时)创建一个名为的变量,该变量的作用域为命名空间/函数。由于创建命名空间时未传递任何值,因此它默认为值 。undefinedundefinedundefinedundefinedundefined

评论

10赞 Darren Lewis 9/13/2011
+1 对于这个伟大的样本。对于任何感兴趣的人,这个样本是 Elijah Manor 在 Mix 2011 上的出色演讲的一部分(忽略标题)live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
13赞 Jared Beck 10/14/2011
从 Elijah 的文章中,以下是这种方法的优缺点,转述一下。优点: 1. 公共和私有属性和方法, 2. 不使用繁琐的 OLN, 3. 保护未定义 4.确保 $ 引用 jQuery, 5。命名空间可以跨越文件, 缺点:比OLN更难理解
6赞 Gustavo Gondim 2/26/2013
这今天称为 IIFE立即调用函数表达式)。感谢您的回答+1!
23赞 mrówa 5/22/2013
@CpILL:不确定是否仍然相关,但第三个参数是值变量的来源。在使用较旧的浏览器/javascript 标准(ecmascript 5、javascript < 1.8.5 ~ firefox 4)时,global-scope 变量是可写的,因此任何人都可以重写其值。添加第三个,您没有传递的附加参数会使它具有价值,因此您正在创建不会被外部源重写的命名空间范围。undefinedundefinedundefinedundefinedundefined
8赞 Mark Amery 6/27/2014
@SapphireSun 这样做的好处是,它允许多个脚本在事先不知道它们将以什么顺序执行时安全地添加到同一个命名空间。如果您希望能够在不破坏代码的情况下任意重新排序脚本包含,或者如果您希望使用 async 属性异步加载脚本,因此无法保证执行顺序,这将很有帮助。查看 stackoverflow.com/questions/6439579/...window.skillet = window.skillet || {}
97赞 Brett Ryan 5/21/2011 #12

我通常在闭包中构建它:

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 语句视为实现的公共接口。

评论

4赞 Paranoid Android 8/12/2014
你不应该检查吗??MYNS.subns = MYNS.subns || {}
0赞 Brett Ryan 8/12/2014
一个很好的观点,应该是对开发人员意图的练习。您需要考虑当它确实存在时该做什么,替换它,出错,使用现有或版本检查并有条件地替换。我遇到过不同的情况,需要每种变体。在大多数情况下,您可能将其作为低风险的边缘情况,并且替换可能是有益的,请考虑一个试图劫持 NS 的流氓模块。
3赞 Soferio 1/30/2015
如果有人有的话,在第 412 页的“Speaking Javascript”一书中,在“快速和肮脏的模块”标题下有对这种方法的解释。
3赞 Braden Best 12/15/2015
优化提示:虽然 和 相似,但私有;由于 JavaScript 的动态类型特性,后者的速度略快,因为它跳过了大多数解释器管道中的一些指令。使用 时,必须调用类型系统以找出分配给所述 var 的类型,而使用 时,类型系统会自动知道它是一个函数,因此跳过了几个函数调用,这意味着对 、 等 CPU 指令的调用更少,这意味着 CPU 流水线更短。var foo = functionfunction foovar foofunction foojmppushqpopq
2赞 Braden Best 12/19/2015
@brett哎呀。你是对的。我在想一种不同的脚本语言。尽管我仍然坚持认为语法更具可读性。我仍然喜欢我的版本。function foo
54赞 Ciarán Bruen 4/22/2012 #13

以下是 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;
};
15赞 dnemoga 4/28/2012 #14

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

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!";
29赞 Jim Jose 5/10/2012 #15

样本:

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;
})()

您可以选择声明变量、 、 like 和 assign (如果希望它是私有的)。localsameselflocal.onTimeout

2赞 haxpanel 12/2/2014 #16

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

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);
8赞 Razan Paul 2/5/2015 #17

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

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/

0赞 Spooky 4/16/2015 #18

我的习惯是使用函数 myName() 作为属性存储,然后使用 var myName 作为“方法”持有者......

不管这是否合法,打败我!我一直依赖我的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:使用 Object.create() 创建对象

1赞 kungfooman 6/13/2015 #19

这是 Ionuț G. Stan 回答的后续,但通过使用 展示了整洁代码的好处,它利用了 JavaScript 的闭包范围,以减少同一命名空间中类的命名空间混乱。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()

输出:

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
1赞 ganesh 8/21/2015 #20

我们可以通过这种方式独立使用它:

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

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

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

A.B.itemTwo = function () {
    //..
}
3赞 nomæd 1/21/2016 #21

最近我最喜欢的模式变成了:

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() {
    // ...
  }
  
})();

当然,return 可以在末尾,但如果只有函数声明跟在它后面,那么就更容易看到命名空间的全部内容,以及公开了什么 API。

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

评论

0赞 olimart 8/25/2016
嗨,您如何从代码片段中调用公共函数?我试过了namespace.a();
0赞 nomæd 9/21/2016
@olivier是的,就是这个想法。虽然现在使用 ES6,但我通常使用对象文字的速记语法 (ponyfoo.com/articles/es6-object-literal-features-in-depth)
0赞 Andres 6/10/2021
我只想强调函数定义末尾的集合。它们是必需的,很容易错过它们。我遇到了与@olimart相同的问题,并通过添加它们解决了它。()
1赞 Saurabh tiwary 10/20/2016 #22

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

注册命名空间 以下是注册命名空间的函数

//Register NameSpaces Function
function registerNS(args){
 var nameSpaceParts = args.split(".");
 var root = window;

 for(var i=0; i < nameSpaceParts.length; i++)
 {
  if(typeof root[nameSpaceParts[i]] == "undefined")
   root[nameSpaceParts[i]] = new Object();

  root = root[nameSpaceParts[i]];
 }
}

要注册命名空间,只需调用上述函数,参数为名称空间,用 (dot) 分隔。 例如 让您的应用程序名称为 oodles。您可以通过以下方法创建命名空间'.'

registerNS("oodles.HomeUtilities");
registerNS("oodles.GlobalUtilities");
var $OHU = oodles.HomeUtilities;
var $OGU = oodles.GlobalUtilities;

基本上,它将在后端创建您的命名空间结构,如下所示:

var oodles = {
    "HomeUtilities": {},
    "GlobalUtilities": {}
};

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

这些变量只不过是初始化命名空间的别名。 现在,每当你声明一个属于你的函数时,都会像下面这样声明它:HomeUtilities

$OHU.initialization = function(){
    //Your Code Here
};

上面是函数名初始化,它被放入一个命名空间中。并在脚本文件中的任意位置调用此函数。只需使用以下代码即可。$OHU

$OHU.initialization();

同样,使用另一个命名空间。

希望对你有所帮助。

10赞 ziesemer 11/28/2016 #23

我迟到了 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 应用)。如果保留其他文件,则可以重用命名空间函数(在上面作为“可选”包含在内)。在最坏的情况下,如果这个函数被重新声明几次 - 它只是几行代码,如果缩小,则更少。

评论

0赞 onefootswill 5/23/2022
这很好,但我有一个关于和的问题。创建命名空间后,是否可以将属性/对象指定为 或 ?就像 - 在我看来,这永远不能被宣布为 .我认为这样我就不能采用一些较新的语言功能。注意:不幸的是,采用新的 Javascript 模块不是一种选择。letconstnamespace("com.ziesemer.myApp")constletcom.ziesemer.myApp.loggerconst
0赞 ziesemer 5/23/2022
@onefootswill - 你要找的东西在这里不适用,因为你不是严格声明变量 - 而是对现有对象的新属性。
0赞 onefootswill 5/24/2022
是的。我意识到这一点。这个问题是由我们的建筑师向我提出的,他正在与我讨论是否包含这种方法。他似乎满足于在吱吱作响的、旧的、低质量的代码库中污染全局命名空间。有些人̄_(ツ)_/ ̄
17赞 Divyanshu Rawat 2/1/2017 #24

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

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

在 JavaScript 中,Module 模式用于进一步模拟类的概念,以便我们能够在单个对象中同时包含公共/私有方法和变量,从而将特定部分屏蔽在全局范围之外。这样可以降低函数名称与页面上其他脚本中定义的其他函数冲突的可能性。

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 );

    }
  };

})();

优势

为什么模块模式是一个不错的选择?对于初学者来说,对于来自面向对象背景的开发人员来说,它比真正的封装要干净得多,至少从 JavaScript 的角度来看是这样。

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

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

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

揭示模块模式

现在我们对模块模式有了更深入的了解,让我们来看看一个稍微改进的版本——Christian Heilmann 的 Revealing Module 模式。

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" );

优势

此模式允许脚本的语法更加一致。它还在模块末尾更清楚地说明了哪些函数和变量可以公开访问,从而简化了可读性。

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

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

16赞 ttt 3/22/2017 #25

如果需要专用范围:If you need the private scope:

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();
6赞 Yairopro 7/5/2018 #26

我认为你们都为这么简单的问题使用了太多的代码。 无需为此制作回购。 这是一个单行函数。

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

尝试一下:

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

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

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

0赞 vishu2124 12/17/2019 #27

默认情况下,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

它始终调用第二个函数定义。在这种情况下,namespace 将解决名称冲突问题。

5赞 Pavel Chuchuva 9/24/2021 #28

ES6 模块命名空间导入

// circle.js
export { name, draw, reportArea, reportPerimeter };
// main.js
import * as Circle from './modules/circle.js';

// draw a circle
let circle1 = Circle.draw(myCanvas.ctx, 75, 200, 100, 'green');
Circle.reportArea(circle1.radius, reportList);
Circle.reportPerimeter(circle1.radius, reportList);

这将获取 circle.js 中所有可用的导出,并使它们可作为对象的成员使用,有效地为其提供自己的命名空间。Circle

1赞 KyleMit 1/12/2022 #29

JavaScript 还没有命名空间的本机表示形式,但 TypeScript 有。

例如,可以使用以下 TS 代码 (playground)

namespace Stack {
    export const hello = () => console.log('hi')
}

Stack.hello()

如果无法将代码更新为 TS,则在生成命名空间的 JS 输出时,至少可以使用 TS 采用的模式,如下所示:

var Stack;
(function (Stack) {
    Stack.hello = () => console.log('hi');
})(Stack || (Stack = {}));
Stack.hello();

延伸阅读: