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


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

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

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


919
2018-05-19 08:11


起源


我可以看看你要去哪里查看命名空间是否被采用,但由于如果失败就不会创建对象我认为更好的方法是警告是否采用命名空间。坦率地说,这应该在大多数JS情况下都不会发生,并且应该在开发中快速捕获。 - annakata
采用顶级“命名空间”(窗口属性)。拥有它。应该在测试的早期检测到冲突。不要费心添加所有这些“假设”检查。 对于重复的“命名空间”来说,这是一个致命的问题,应该这样对待。您可以按照像jQuery这样的方法来允许驻留自定义“命名空间”;但这是 仍然 设计时问题。
请将您接受的答案更改为 stackoverflow.com/questions/881515/... ,这是一个更优雅和更新的解决方案。 - hlfcoding
@pst YUI的作用是什么?我相信他们这样做是为了逐步增加他们的命名空间。肯定会在HTTP环境中获得这样的技巧吗? - Simon_Weaver
也可以看看 stackoverflow.com/questions/2102591/... 对于性能问题 - Tim Abell


答案:


我喜欢这个:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

713
2018-05-19 08:22



重要的一点是,在扩展中只比一个根变量更具宗教性。 一切 必须从这里流出来。 - annakata
这不会为你的代码创建一个闭包 - 调用你的其他函数会很麻烦,因为它们总是看起来像:yourNamespace.bar();我做了一个开源项目JUST来解决这个设计问题: github.com/mckoss/namespace。 - mckoss
annakata:“重要的一点就是要扩大一个根变量的宗教信仰。” - 为什么会这样? - user406905
@alex - 为什么应该有一个浅的对象结构? - Ryan
@Ryan我的意思是一切都应该在 MyApp,例如 MyApp.Views.Profile = {} 而不是 MyApp.users = {} 和 MyViews.Profile = {}。不一定只有两个级别的深度。 - alex


我用 在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 论据

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


1015
2018-05-10 08:28



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


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

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

以上就是这样的 模块模式 和 不管你喜不喜欢,它允许您将所有函数公开为public,同时避免对象文字的刚性结构。


334
2018-05-19 08:39



1. OLN和模块模式之间存在差异。 2.我不/永远/喜欢OLN,因为你必须记住不要放最后的尾随逗号,所有的属性必须用值初始化(如null或undefined)。此外,如果需要成员函数的闭包,那么每个方法都需要小函数工厂。另一件事是你必须将所有控制结构都包含在函数中,而上面的表格并没有强制执行。这并不是说我不使用OLN,只是有时我不喜欢它。 - Ionuț G. Stan
我喜欢这种方法,因为它允许私有函数,变量和伪常量(即var API_KEY = 12345;)。 - Lawrence Barsanti
我喜欢这个比逗号更高的逗号分隔对象容器更好。相比之下,我也没有看到任何缺点。我错过了什么吗? - Lucent
JS新手在这里......为什么我不需要输入 ns().publicFunction(), 那是... ns.publicFunction() 作品。 - John Kraft
@John Kraft,这是因为 new 前面的关键字 function 关键词。基本上,正在做的是它声明一个匿名函数(作为一个函数,它也是一个构造函数),然后它立即调用它作为构造函数使用 new。因此,存储在里面的最终值 ns 是匿名构造函数的(唯一)实例。希望它有意义。 - Ionuț G. Stan


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

是。例如:

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

154
2018-05-26 11:34



这给了我IE7中的错误。 var your_namespace =(typeof your_namespace ==“undefined”||!your_namespace)? {}:your_namespace;效果更好。 - mjallday
它应该是var your_namespace = your_namespace = your_namespace || {}适用于每个浏览器;) - Palo
@Palo你能解释一下为什么会这样吗? var your_namespace = your_namespace = your_namespace || {} - Sriram
您可以在不同的js文件中扩展your_namespace对象。使用var your_namespace = {}时,您无法执行此操作,因为每个文件都会覆盖该对象 - Alex Pacurar
这是肯定的最佳答案。 - earl3s


我通常在一个闭包中构建它:

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


88
2018-05-20 20:07



你不应该检查 MYNS.subns = MYNS.subns || {} ?? - Mirko
一个好点,应该是开发者意图的练习。您需要考虑当它存在时要做什么,替换它,错误,使用现有或版本检查并有条件地替换。我有不同的情况需要每个变种。在大多数情况下,您可能将此作为低风险边缘情况并且替换可能是有益的,考虑一个试图劫持NS的流氓模块。 - Brett Ryan
如果有人在“Quick and Dirty Modules”标题下,可以在第412页的“Speaking Javascript”一书中解释这种方法。 - Soferio
优化提示:同时 var foo = function 和 function foo 是相似的,是私人的;由于JavaScript的动态类型,后者是 略 更快,因为它在大多数解释器的管道中跳过一些指令。同 var foo,类型系统必须被调用以找出分配给所述var的类型,而使用 function foo,类型系统自动知道它是一个函数,因此跳过几个函数调用,转换为更少的CPU指令调用,如 jmp, pushq, popq等,转换为更短的CPU管道。 - Braden Best
@brett oops。你是对的。我在想一种不同的脚本语言。虽然我仍然坚持 function foo 语法更具可读性。我仍然喜欢我的版本。 - Braden Best


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

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


55
2017-11-09 04:27



我也使用这种方法处理多个文件 - Mirko
我发现这是一个非常有用的方法,可以将客户端脚本组织成大型应用程序中的多个文件,其中功能需要模块化。 - DVK
问题专门针对多个文件: stackoverflow.com/questions/5150124/... - Ciro Santilli 新疆改造中心 六四事件 法轮功


这就是Stoyan Stefanov在他的作品中的表现 JavaScript模式 我发现它非常好的书(它还显示了他如何做出允许自动生成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;
};

46
2018-04-22 15:44





我用这种方法:

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

32
2018-05-19 08:26



很详细!谢谢!只是想知道你对Namespace.js有什么看法。我自己从未使用过它,所以我想知道有你的知识/技能/经验的人是否会考虑使用它。 - John
我喜欢!另一方面,我在这个外部代码的第一行得到异常,说:'myNameSpace.MyClass'[undefined]不是构造函数。也许这取决于JS实现? :/ - yoosiba
@yossiba:可能。上面的代码是相当标准的东西。在标准JS中,任何函数都可以用作构造函数,没有什么需要做的就是将函数标记为专门用作构造函数。你是否使用像ActionScript这样的不寻常的味道? - AnthonyWJones
@Anthony最好使用var MYNAMESPACE = MYNAMESPACE || {};只使用var myNamespace = {}是不安全的,而且最好以大写字母声明你的命名空间 - paul
@paul:“更好”可能非常主观。我讨厌阅读SHOUTS给我的代码,所以我避免使用全部使用大写的标识符。同时 ns = ns || {} 可能看起来更具防御性,可能导致其他意外结果。 - AnthonyWJones


这是user106826指向Namespace.js的链接的后续内容。项目似乎转移到了 GitHub上。就是现在 史密斯/ namespacedotjs

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

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

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

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

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

有关更多用法示例,请查看example.js文件 来源


32
2017-08-27 23:15



只要你记得这有一些性能影响,每次你访问my.awesome.package.WildClass时,你都会访问我的awesome属性,my.awesome的package属性和my.awesome的WildClass属性。包。 - SamStephens


样品:

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 如果你想要它是私人的。


29
2018-05-10 11:44