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


990

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

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

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


20
我可以查看检查名称空间是否被使用的地方,但是由于如果失败则不会创建对象,所以我认为更好的方法是警告名称空间是否被使用。坦率地说,这在大多数JS情况下应该不会发生,并且应该在开发中迅速发现。
annakata

18
取得顶层的“命名空间”(window属性)。拥有它。冲突应在测试中及早发现。不要费心添加所有这些“如果”检查。对于重复的“命名空间”来说,这是一个致命的问题,应该这样对待。您可以遵循类似jQuery的方法来允许使用自定义“名称空间”;但这仍然是设计时的问题。

请将您接受的答案更改为stackoverflow.com/questions/881515/…,这是一种更为优雅和更新的解决方案。
hlfcoding

@pst YUI做什么?我相信他们正是这样做,以便逐步添加到其命名空间中。这样的技巧一定是HTTP环境中的性能所必需的吗?
Simon_Weaver

Answers:


764

我喜欢这个:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
重要的一点是要虔诚地扩大一个根本的变量。一切都必须由此产生。
annakata

22
这不会为您的代码创建一个闭包-调用其他函数很麻烦,因为它们始终必须看起来像:yourNamespace.bar();。我创建了一个开源项目来解决这个设计问题:github.com/mckoss/namespace
mckoss 2011年

24
annakata:“重要的一点是,要虔诚地扩大一个根变量。”-这是为什么呢?
user406905 2011年

11
@alex-为什么应该有一个浅对象结构?
瑞安

25
@Ryan我的意思是所有内容都应该在下面MyApp,例如,MyApp.Views.Profile = {}而不是MyApp.users = {}MyViews.Profile = {}。不一定只有两个层次的深度。
alex 2012年

1042

我使用在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参数是value变量的来源undefined。我不确定今天是否仍然有用,但是在使用较旧的浏览器/ JavaScript标准(ecmascript 5,javascript <1.8.5〜firefox 4)时,global-scope变量undefined是可写的,因此任何人都可以重写其值。第三个参数(未传递值时)创建一个名为的变量undefined,其作用域为名称空间/函数。因为在创建名称空间时未传递任何值,所以它默认为value undefined


9
为这个很棒的样本+1。对于任何有兴趣的人,此样本都是Elijah Manor在Mix 2011上出色演讲的一部分(忽略标题)live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis

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

4
今天称为IIFE立即调用函数表达式)。感谢您的回答+1!
古斯塔沃·冈迪姆

20
@CpILL:不确定是否仍然有用,但是第三个undefined参数是value变量的来源undefined。在使用较旧的浏览器/ javascript标准(ecmascript 5,javascript <1.8.5〜firefox 4)时,global-scope变量undefined是可写的,因此任何人都可以重写其值。添加未传递的第三个附加参数使它成为value undefined,因此您创建的是命名空间范围undefined,不会被外部源重写。
mrówa

4
@SapphireSun的好处window.skillet = window.skillet || {}是,当它们事先不知道执行顺序时,可以允许多个脚本安全地添加到同一名称空间。如果您希望能够在不破坏代码的情况下随意重新排序脚本包含的内容,或者想要通过async属性异步加载脚本,因此不能保证执行顺序,那么这将很有帮助。见stackoverflow.com/questions/6439579/...
马克·阿梅里奥

338

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

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

上面的代码非常类似于模块模式无论您是否喜欢它,它都可以将所有函数公开为公共,同时避免对象文字的僵化结构。


16
1. OLN和模块模式之间有区别。2.我不总是喜欢OLN,因为您要记住不要放在最后一个逗号后面,并且必须使用值(例如null或undefined)初始化所有属性。另外,如果需要成员函数的闭包,则每个方法都需要小型函数工厂。另一件事是,必须将所有控件结构都封装在函数内部,而上述形式并不强加于此。这并不是说我不使用OLN,而是有时候我不喜欢它。
Ionuț G. Stan,2009年

8
我喜欢这种方法,因为它允许使用私有函数,变量和伪常量(即var API_KEY = 12345;)。
劳伦斯·巴尔桑蒂

12
我比投票更高的逗号分隔对象容器更好。与之相比,我也看不到任何缺点。我想念什么吗?
朗讯

7
JS Newbie在这里...为什么不用键入ns().publicFunction(),即... ns.publicFunction()有效。
约翰·卡夫

14
@John Kraft,这是因为new关键字位于关键字前面function。基本上,这样做是在声明一个匿名函数(作为一个函数,它也是一个构造函数),然后立即使用调用它作为构造函数new。这样,存储在其中的最终值ns是该匿名构造函数的(唯一)实例。希望有道理。
Ionuț G. Stan

157

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

是。例如:

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
这给我IE7错误。var your_namespace =(typeof your_namespace ==“未定义” ||!your_namespace)?{}:your_namespace;效果更好。
2010年

9
它应该是var your_namespace = your_namespace = your_namespace || {}适用于所有浏览器;)
Palo

向我+1!通过将一个库扩展到不同的文件或同一文件中的不同位置,Thin一个可以作为Jaco Pretorius的答案。太棒了!
centurian

2
@Palo您能解释为什么会这样吗?var your_namespace = your_namespace = your_namespace || {}
斯里拉姆

6
您将有可能在不同的js文件中扩展your_namespace对象。使用var your_namespace = {}时,您不能这样做,因为该对象将被每个文件覆盖
Alex Pacurar 2014年

92

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

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


3
你不应该检查MYNS.subns = MYNS.subns || {}吗?
Mirko,2014年

好的一点应该是锻炼开发人员的意图。您需要考虑当它存在时该怎么做,替换它,出错,使用现有的或版本检查并有条件地替换。我遇到了不同的情况,需要每种变体。在大多数情况下,您可能会将其作为低风险的优势案例,而替换可能会有所帮助,请考虑尝试劫持NS的恶意模块。
Brett Ryan

1
在第412页的“口语Javascript”一书中,如果有的话,在“快速和肮脏模块”标题下对此方法进行了说明。
Soferio

2
优化提示:var foo = function和和function foo相似,是私有的;由于JavaScript具有动态类型化的特性,因此后者会快一些,因为它会跳过大多数解释器管道中的一些指令。使用var foo,类型系统必须被调用以找出分配给所述var function foo的类型,而使用,类型系统会自动知道它是一个函数,因此跳过了几个函数调用,这转化为更少的CPU指令调用,例如jmppushqpopq等,其转化为更短的CPU流水线。
Braden Best 2015年

1
@brett oops。你是对的。我在想一种不同的脚本语言。尽管我仍然坚持function foo语法更易读。而且我仍然喜欢我的版本。
Braden Best

56

因为您可能编写了不同的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


48

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

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

很详细!谢谢!只是想知道您对Namespace.js有什么看法。我从来没有亲自使用过它,所以我想知道是否有您的知识/技能/经验的人会考虑使用它。
约翰,2010年

我喜欢!另一方面,我在此外部代码的第一行出现异常,说:“ myNameSpace.MyClass” [未定义]不是构造函数。也许这取决于JS的实现?:/
yoosiba

@yossiba:可能。上面的代码是相当标准的东西。在标准JS中,任何函数都可以用作构造函数,而无需将函数标记为专门用作构造函数。您是否正在使用诸如ActionScript之类的非凡风味?
AnthonyWJones

@Anthony最好使用var MYNAMESPACE = MYNAMESPACE || {}; 仅使用var myNamespace = {}是不安全的,而且最好在caps中声明名称空间
paul,

9
@paul:“更好”可以是相当主观的。我讨厌阅读对我大声疾呼的代码,因此避免使用使用全部大写字母的标识符。尽管ns = ns || {}看上去更具防御性,但它可能导致其他意外结果。
AnthonyWJones

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

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


2
只要您还记得这对性能有影响,每次访问my.awesome.package.WildClass时,您都在访问my的awesome属性,my.awesome的package属性和my.awesome的WildClass属性。包。
SamStephens

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是否为私有变量。


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

13

如果需要私有范围:

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

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

在使用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 );

    }
  };

})();

优点

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

其次,它支持私有数据-因此,在模块模式下,我们代码的公共部分可以触摸私有部分,但是外界无法触摸类的私有部分。

缺点

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

我们也不能在稍后的方法中访问私有成员。也就是说,在许多情况下,模块模式仍然非常有用,并且在正确使用时,肯定有潜力改善我们应用程序的结构。

显示模块模式

现在,我们对模块模式有了更多的了解,让我们看一下稍作改进的版本-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" );

优点

这种模式可以使脚本的语法更加一致。它还使在模块末尾可以更清楚地知道我们的哪些函数和变量可以公开访问,从而简化了可读性。

缺点

这种模式的缺点是,如果私有功能引用了公共功能,那么在需要补丁的情况下,该公共功能不能被覆盖。这是因为私有函数将继续引用私有实现,并且该模式不适用于公共成员,而仅适用于函数。

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


9

我创建了受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

})();

8

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

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

有关好处的说明,请参阅我的博客文章。您可以在此处获取源代码

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


1
:我创建了命名库的改进版本(2.0) code.google.com/p/pageforest/source/browse/appengine/static/src/...
mckoss

您所有的链接似乎都死了
snoob dogg

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/


8

我参加晚会晚了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应用程序)。如果保留,其他文件则可以重用名称空间功能(在上面作为“可选”提供)。最糟糕的是,如果多次声明此函数-只需几行代码,如果缩小则更少。


2

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

(function ($, undefined) {

    console.log(this);

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

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。

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


嗨,您如何从代码段中调用公共功能?我已经尝试过namespace.a();
olimart

@olivier是的,就是这个想法。虽然现在ES6,我通常使用对象常量的语法速记(ponyfoo.com/articles/es6-object-literal-features-in-depth
Nomaed

2

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

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


1

如果使用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任务“执行”,然后可以“ make go”在编写代码时使构建保持更新。


1

很好地跟进了Ionuț G. Stan的答案,但是通过使用展示了代码整洁的好处var ClassFirst = this.ClassFirst = function() {...},该方法利用了JavaScript的闭包作用域来减少同一名称空间中类的名称空间混乱。

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

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

文件hello.js

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

文件Example.js

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

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

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


1
@ peter-mortensen这些对我'11的答案的修改真的必要吗?您在做什么绝对不是故意破坏,别误会我的意思,但是它们非常肤浅。除非您确实添加了一些不错的东西,否则我宁愿保留此类文章的唯一作者。
Stijn de Witt 2015年

1

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

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

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

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

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

0

我的习惯是使用函数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()创建对象


0

在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]];
 }
}

要注册一个名称空间,只需调用上面的函数,并将参数作为名称空间并用'.'(点)分隔即可。例如,让您的应用程序名称为oodles。您可以通过以下方法创建名称空间

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

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

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

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

这些变量不过是初始化名称空间的别名。现在,每当您声明一个属于您的函数时,HomeUtilities都将如下声明:

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

上面是函数名的初始化,并将其放入命名空间中$OHU。并在脚本文件中的任何位置调用此函数。只需使用以下代码。

$OHU.initialization();

同样,与另一个NameSpaces。

希望能帮助到你。


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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.