问答中心分类: JAVASCRIPT如何在JavaScript中声明命名空间?
0
匿名用户 提问 18小时 前

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

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

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

28 Answers
0
Jaco Pretorius 回答 18小时 前

我使用在企业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.

Darren Lewis 回复 18小时 前

+这是一个很好的例子。对于任何感兴趣的人来说,这个样本是Elijah Manor在2011年Mix大会上精彩演讲的一部分(忽略标题)居住访问Mix。com/MIX11/Sessions/Speaker/Elijah Manor

Jared Beck 回复 18小时 前

从以利亚的文章中,这里是这种方法的利弊,转述。优点:1。公共和私有属性和方法,2。不使用笨重的OLN,3。保护未定义的4。确保$引用jQuery,5。命名空间可以跨文件,缺点:比OLN更难理解

Antoine 回复 18小时 前

杰出的但是,如果jQuery尚未加载,它将失败。是否有一种方法可以执行,并且在那里测试它并在必要时加载?

Ryan 回复 18小时 前

@安托万-为什么会失败?jQuery应该是依赖项,应该在脚本之前加载

Antoine 回复 18小时 前

@Ryan:我在一个bookmarklet中调用它,在那里我不测试jQuery(想要让bookmarklet尽可能简单,逻辑是在服务器端完成的)。我通过删除$parameter并在名称空间内测试/加载jQuery来克服这个问题。

Gustavo Gondim 回复 18小时 前

这就是今天生活(立即调用的函数表达式). 谢谢你的回答+1!

Gustavo Gondim 回复 18小时 前

@CpILL第三个ùndefined`参数用于确保没有其他函数传递第三个参数并修改行为。这是一种保证。

mrówa 回复 18小时 前

@CpILL:不确定是否仍然相关,但第三个,undefined参数是值变量的来源undefined. 在使用较旧的浏览器/javascript标准(ecmascript 5,javascript<1.8.5 ~ firefox 4)时,全局范围变量undefined是可写的,因此任何人都可以重写其值。添加第三个,您没有传递的附加参数使其更有价值undefined,因此您正在创建命名空间范围undefined不会被外界改写。

SapphireSun 回复 18小时 前

所以我真的不明白窗户的用途。煎锅=窗户。煎锅| |{}。为什么您希望另一个文件意外地附加到煎锅的定义中?难道你不希望它故意这么做吗?我宁愿看到一个大错误弹出。。。。也许这是为了当人们想添加扩展到您的模块,但你不知道哪一个是加载第一?

Jay Sullivan 回复 18小时 前

@赛菲生:谁说这一定是意外?该语法允许您在多个位置扩展该类。

Laoujin 回复 18小时 前

如果你感到困惑,@Jared Beck对这篇文章进行了极好的正反两方面的分析。也许还有一个缺点:不要仅仅因为它更好就使用它。公认答案中解释的{}OLN(对象文字符号)非常适合于简单场景。

Mark Amery 回复 18小时 前

@SapphireSun的好处window.skillet = window.skillet || {}它允许多个脚本在事先不知道执行顺序的情况下安全地添加到同一命名空间。如果您希望能够在不破坏代码的情况下对脚本包含进行任意重新排序,或者希望使用异步加载脚本,那么这将非常有用异步属性因此无法保证执行顺序。看见堆栈溢出。com/questions/6439579/…

Anthony Mason 回复 18小时 前

我还要加一个例子;我推荐您使用这个示例代码@MarkAmery,我不得不说,你的评论可能是javascript结构中最容易被误解的组件之一,你提供了一个很好的解释,说明了每个函数/变量的范围以及如何调用它们。

Stijn de Witt 回复 18小时 前

这样做还有一个很大的缺点(imho)namespace.func = function(){ /* ... */};:所有这些函数最终都是匿名的. 调试、查看堆栈跟踪等时会带来不便。请考虑停止编写匿名函数. (我是那篇博文的作者)。

daveruinseverything 回复 18小时 前

这个例子中提供的答案有一个bug——我找不到解决这个问题的方法。在最初的实施中skillet,一个私有变量isHot已声明。随后skillet扩展以添加toString()方法,在这个扩展中是私有变量toHot被引用。在我的测试中,这是不可能的,因为来自起初的在后续扩展中无法引用实现。将此答案中的代码粘贴到浏览器的JS控制台中。skillet.toString()失败:ReferenceError:找不到变量:isHot

aamarks 回复 18小时 前

@daveruinseverything答案中引用的站点上的完整示例表明,这会导致错误。好处是可以创建第二个闭包?扩展名称空间,但闭包仍然无法访问另一个的私有变量,但这也意味着您不必担心您的私有变量与另一个中的私有变量冲突。(我刚刚开始理解这一点,所以可能有一些错误。)

0
Ionuț G. Stan 回答 18小时 前

另一种方法是这样做,我认为它比对象文字形式限制性小一些:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

上面很像模块模式不管你喜欢与否,它允许您将所有函数公开为公共函数,同时避免对象文字的僵硬结构。

annakata 回复 18小时 前

任何人都不会爱奥尔恩,这让我有点目瞪口呆。我只是。。。什么是不爱?什么东西这么僵硬?

Ionuț G. Stan 回复 18小时 前

1.OLN和模块模式之间存在差异。我不/总是/喜欢OLN,因为你必须记住不要放最后一个尾随逗号,你的所有属性都必须用一个值初始化(比如null或未定义)。此外,如果您需要成员函数的闭包,那么对于这些方法中的每一种,您都需要小的函数工厂。另一件事是,您必须将所有控制结构封装在函数中,而上面的形式并没有强制要求。这并不是说我不使用OLN,只是有时候我不喜欢它。

Titi Wangsa bin Damhore 回复 18小时 前

这种风格与我的javascript编辑器(eclipse)兼容,如中所示,它可以自动格式化和缩进。

Lawrence Barsanti 回复 18小时 前

我喜欢这种方法,因为它允许私有函数、变量和伪常量(即var API_KEY=12345;)。

Lucent 回复 18小时 前

我更喜欢这个,而不是被投票选得更高的逗号分隔对象容器。相比之下,我也没有发现任何缺点。我错过什么了吗?

Omu 回复 18小时 前

有人知道为什么jQuery不使用这种方法,而是使用逗号分隔的方法吗?

Ionuț G. Stan 回复 18小时 前

此模式适用于单例对象,但jQuery是构造函数。在我的例子中,你不能打电话ns(),jQuery需要这个。

Adi Roiban 回复 18小时 前

现在,如果JSLint不抱怨这种合法的结构,一切都会好得多:)

John Kraft 回复 18小时 前

JS新手在这里。。。为什么我不用打字ns().publicFunction(),即。。。ns.publicFunction()作品

Ionuț G. Stan 回复 18小时 前

@约翰·卡夫,这是必要的new关键字前面的function关键字。基本上,它所做的是声明一个匿名函数(作为一个函数,它也是一个构造函数),然后使用new. 因此,存储在其中的最终值ns是该匿名构造函数的(唯一)实例。希望它有意义。

BumbleB2na 回复 18小时 前

这种方法有点怪:如果你想访问“内部”方法中的“公共”成员或方法,你必须添加“类”名称。示例:ns。publicFunction();或ns。公共成员;

Flash 回复 18小时 前

同样值得一提的是,在上述示例中this指您在publicFunction,但指的是中的全局对象internalFunction. 我通常会加上var self = this在顶部,以避免混淆。

0
Alex Pacurar 回答 18小时 前

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

对例如:

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);
}
mjallday 回复 18小时 前

这给了我IE7中的一个错误。var your_namespace=(your_namespace的类型==“undefined”| |!your_namespace)?{}:您的_名称空间;效果更好。

Palo 回复 18小时 前

它应该是var your_namespace=your_namespace=your_namespace | |{}适用于每个浏览器;)

centurian 回复 18小时 前

+1来自我!Thin one就像Jaco Pretorius answer一样,将一个库扩展到不同的文件或同一文件中的不同位置。太棒了!

Sriram 回复 18小时 前

@帕洛,你能解释一下为什么会这样吗?var your_namespace = your_namespace = your_namespace || {}

Jack 回复 18小时 前

这有什么优势var your_namespace = {};?

Alex Pacurar 回复 18小时 前

您可以在不同的js文件中扩展your_名称空间对象。当使用var your_namespace={}时,您不能这样做,因为对象将被每个文件覆盖

jropella 回复 18小时 前

根据我的经验,这实际上是商业网站开发公司这样做的“标准”方式。就我而言,这是在javascript中实现“名称空间”范式的最清晰、最简洁、最可读的方法。

aamarks 回复 18小时 前

然而MDN不鼓励使用with?

0
Brett Ryan 回答 18小时 前

我通常在闭包中构建它:

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和实现更容易理解。将返回语句视为实现的公共接口。

Mirko 回复 18小时 前

你不应该检查MYNS.subns = MYNS.subns || {}??

Brett Ryan 回复 18小时 前

这一点很好,应该是对开发人员意图的练习。您需要考虑当它确实存在时要做什么,替换它、出错、使用现有或版本检查并有条件地替换。我遇到过需要每种变体的不同情况。在大多数情况下,这可能是一种低风险的边缘情况,更换可能是有益的,请考虑一个试图劫持NS的恶意模块。

Soferio 回复 18小时 前

关于这种方法的解释,请参阅第412页的《说Javascript》一书,标题为“快速脏模块”。

Brett Ryan 回复 18小时 前

我必须去看看。是不是因为标题中的“肮脏”而说了否定的话?英雄联盟

Braden Best 回复 18小时 前

优化提示:whilevar foo = functionfunction foo相似,私人;由于JavaScript的动态类型特性,后者是轻微地速度更快,因为它跳过了大多数口译员管道中的几个指令。具有var foo,类型系统必须被调用,以找出分配给所述var的类型,而function foo,类型系统自动知道它是一个函数,因此跳过了几个函数调用,这意味着更少的CPU指令调用,如jmp,pushq,popq等等,这意味着CPU管道更短。

Braden Best 回复 18小时 前

此外,我认为function foovar foo = function,因为即使var语法允许垂直对齐function语法是明确的,阅读代码的人可以立即理解。这个var语法会导致稍长的心理分析。所以在某种程度上function针对两个CPU进行语法优化大脑管道。尼托!这只是一个品味的问题,但我会这样做最后一个片段:pastebin。com/ZK49AJ9. 这很微妙,但我觉得读起来更快。

Brett Ryan 回复 18小时 前

@B1K音乐这是不正确且糟糕的建议。这两者之间有着明显的区别。使用赋值在运行时声明函数,允许延迟和条件函数声明,使用function myFunc在脚本求值时定义,因此不能有条件地声明。如果你能以任何方式证明你的主张是有效的,那么请提供它,你可能会发现事实恰恰相反。对于只定义一次的东西,无论如何你都不应该尝试优化它。

Braden Best 回复 18小时 前

@布雷特哎呀。你说得对。我在考虑另一种脚本语言。尽管我仍然坚持function foo语法更具可读性。我仍然喜欢我的版本。

0
Fentex 回答 18小时 前

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

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

DVK 回复 18小时 前

我发现这是一种非常有用的方法,可以在功能需要模块化的大型应用程序中将客户端脚本组织到多个文件中。

专门针对多个文件提出的问题:堆栈溢出。com/questions/5150124/…