【问题标题】:How do I declare a namespace in JavaScript?如何在 JavaScript 中声明命名空间?
【发布时间】:2010-10-27 06:15:34
【问题描述】:

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

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

有没有更优雅或更简洁的方式来做到这一点?

【问题讨论】:

  • 我可以看到你要去哪里检查命名空间是否被占用,但由于如果失败将不会创建对象,我认为更好的方法是在命名空间是否被使用时发出警报采取。坦率地说,这不应该在大多数 JS 情况下发生,并且应该在开发中迅速发现。
  • 采用顶级“命名空间”(窗口属性)。拥有它。应该在测试的早期发现冲突。不要费心添加所有这些“假设”检查。 重复的“命名空间”是一个致命的问题,应该这样对待。您可以遵循 jQuery 之类的方法来允许使用自定义“命名空间”;但这仍然是设计时问题。
  • 有关性能问题,另请参阅 stackoverflow.com/questions/2102591/…
  • 另见stackoverflow.com/questions/4125479/…对象与函数命名空间
  • 嗯,现在我们有符号和模块,所以重复的命名空间应该不是问题。

标签: javascript namespaces javascript-namespaces


【解决方案1】:

我喜欢这个:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

【讨论】:

  • 重要的一点是对扩展不超过一个根变量保持虔诚。 一切都必须由此而来。
  • 这不会为你的代码创建一个闭包——它使得调用你的其他函数变得乏味,因为它们总是看起来像:yourNamespace.bar();我做了一个开源项目只是为了解决这个设计问题:github.com/mckoss/namespace
  • annakata:“重要的一点是要坚持只扩展一个根变量。”- 这是为什么?
  • @alex - 为什么要有浅层对象结构?
  • @Ryan 我的意思是一切都应该在MyApp 下,例如MyApp.Views.Profile = {} 而不是 MyApp.users = {}MyViews.Profile = {}。不一定只有两个级别的深度。
【解决方案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);

【讨论】:

  • 非常详细!谢谢!只是想知道您对 Namespace.js 的看法。我自己从来没有使用过它,所以我想知道有你的知识/技能/经验的人是否会考虑使用它。
  • 我喜欢!另一方面,我在此外部代码的第一行遇到异常,说:'myNameSpace.MyClass' [undefined] 不是构造函数。也许它取决于JS实现? ://
  • @yossiba:可能。上面的代码是相当标准的东西。在标准 JS 中,任何函数都可以用作构造函数,无需将函数标记为专门用作构造函数。您是否使用了 ActionScript 之类的不寻常风格?
  • @Anthony 最好使用 var MYNAMESPACE = MYNAMESPACE || {};仅使用 var myNamespace = {} 是不安全的,而且最好在大写字母中声明您的命名空间
  • @paul:“更好”可能是相当主观的。我讨厌阅读对我大喊大叫的代码,所以我避免使用全部大写的标识符。虽然ns = ns || {} 可能看起来更具防御性,但它可能会导致其他意想不到的结果。
【解决方案3】:

另一种方法是:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

上面很像the module patternwhether you like it or not,它允许你公开你所有的函数,同时避免对象字面量的刚性结构。

【讨论】:

  • 1. OLN 和模块模式是有区别的。 2.我不/总是/喜欢OLN,因为您必须记住不要放置最后一个尾随逗号,并且您的所有属性都必须用一个值(如null或undefined)初始化。此外,如果您需要成员函数的闭包,则每个方法都需要小型函数工厂。另一件事是您必须将所有控制结构包含在函数中,而上述形式并没有强加这一点。这并不是说我不使用OLN,只是有时我不喜欢它。
  • 我喜欢这种方法,因为它允许私有函数、变量和伪常量(即 var API_KEY = 12345;)。
  • 我更喜欢这个,而不是投票更高的逗号分隔对象容器。相比之下,我也没有看到任何缺点。我错过了什么吗?
  • 这里的JS新手...为什么我不用输入ns().publicFunction(),也就是说...ns.publicFunction()有效。
  • @John Kraft,这是因为 new 关键字位于 function 关键字前面。基本上,它所做的是声明一个匿名函数(作为一个函数,它也是一个构造函数),然后它立即使用new 作为构造函数调用它。因此,存储在ns 中的最终值是该匿名构造函数的(唯一)实例。希望这是有道理的。
【解决方案4】:

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

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

福利的描述在我的blog post。你可以抓住source code here

我真正喜欢的好处之一是模块之间在加载顺序方面的隔离。您可以在加载外部模块之前对其进行引用。而你得到的对象引用会在代码可用时填写。

【讨论】:

【解决方案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);
}

【讨论】:

  • 这给了我在 IE7 中的错误。 var your_namespace = (typeof your_namespace == "undefined" || !your_namespace ) ? {}:你的命名空间;效果更好。
  • 应该是 var your_namespace = your_namespace = your_namespace || {} 适用于所有浏览器;)
  • @Palo 你能解释一下为什么会这样吗? var your_namespace = your_namespace = your_namespace || {}
  • 您可以在不同的 js 文件中扩展 your_namespace 对象。使用 var your_namespace = {} 时,您不能这样做,因为该对象将被每个文件覆盖
  • 还有MDN discourages使用with吗?
【解决方案6】:

我创建了 namespace,它的灵感来自 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

})();

【讨论】:

    【解决方案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.');
            }
        }
    });
    

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

    【讨论】:

    • 只要你记得这对性能有一些影响,因为每次访问 my.awesome.package.WildClass 时,你都在访问 my.awesome 的 awesome 属性,my.awesome 的 package 属性,并且my.awesome.package 的 WildClass 属性。
    【解决方案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(){}
    

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

    【讨论】:

    • 我发现这是一种非常有用的方法,可以在需要模块化功能的大型应用程序中将客户端脚本组织成多个文件。
    • 专门询问多个文件的问题:stackoverflow.com/questions/5150124/…
    【解决方案9】:

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

    文件 hello.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中找到相关项目。

    【讨论】:

    • @peter-mortensen 这些对我 11 年回答的修改真的有必要吗?你在做什么绝对不是故意破坏,不要误会我的意思,但它们非常肤浅。除非您真的添加了一些好的东西,否则我宁愿继续成为此类帖子的唯一作者。
    【解决方案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 任务“开始”,您可以“开始”以在编写代码时保持构建更新。

    【讨论】:

      【解决方案11】:

      我用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 undefined 是可写的,所以任何人都可以重写它价值。第三个参数(当未传递值时)创建一个名为 undefined 的变量,其作用域为命名空间/函数。因为创建命名空间时没有传递任何值,所以它默认为值undefined

      【讨论】:

      • +1 为这个伟大的样本。对于任何感兴趣的人,这个样本是 Elijah Manor 在 Mix 2011 上的精彩演讲的一部分(忽略标题)live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
      • 从以利亚的文章中,这里是这种方法的利弊,解释。优点:1. 公共和私有属性和方法,2. 不使用繁琐的 OLN,3. 保护未定义 4. 确保 $ 引用 jQuery,5. 命名空间可以跨文件,缺点:比 OLN 更难理解
      • 今天称为 IIFE立即调用函数表达式)。感谢您的回答 +1!
      • @CpILL:不确定是否仍然相关,但第三个,undefined 参数是值变量undefined 的来源。在使用旧版浏览器/javascript 标准(ecmascript 5,javascript undefined 是可写的,因此任何人都可以重写它的值。添加第三个,您未传递的附加参数使其值为undefined,因此您正在创建命名空间范围undefined,它不会被外部资源重写。
      • @SapphireSun window.skillet = window.skillet || {} 的好处是它允许多个脚本在事先不知道它们将执行的顺序时安全地添加到同一个命名空间。如果您希望能够在不破坏代码的情况下任意重新排序脚本包含,或者如果您想使用 the async attribute 异步加载脚本并且因此无法保证执行顺序,这将很有帮助。见stackoverflow.com/questions/6439579/…
      【解决方案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 语句视为实现的公共接口。

      【讨论】:

      • 你不应该检查MYNS.subns = MYNS.subns || {}吗??
      • 很好的一点应该是对开发人员意图的练习。您需要考虑当它确实存在时该怎么做,替换它,出错,使用现有或版本检查并有条件地替换。我遇到了需要每种变体的不同情况。在大多数情况下,您可能认为这是一个低风险的边缘情况,替换可能是有益的,考虑一个试图劫持 NS 的恶意模块。
      • 如果有人知道的话,在“Speaking Javascript”一书第 412 页的“快速和肮脏的模块”标题下有对这种方法的解释。
      • 优化提示:var foo = functionfunction foo相似,都是私有的;由于 JavaScript 的动态类型特性,后者稍微快一些,因为它跳过了大多数解释器管道中的一些指令。使用var foo,必须调用类型系统以找出分配给所述var的类型,而使用function foo,类型系统会自动知道它是一个函数,因此会跳过几个函数调用,转换为更少的 CPU 指令调用,如 jmppushqpopq 等,这意味着更短的 CPU 流水线。
      • @brett 哎呀。你说得对。我正在考虑一种不同的脚本语言。尽管我仍然坚持 function foo 语法更具可读性。而且我仍然喜欢我的版本。
      【解决方案13】:

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

      /**
      * 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;
      };
      

      【讨论】:

        【解决方案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!";
        

        【讨论】:

          【解决方案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;
          })()
          

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

          【讨论】:

            【解决方案16】:

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

            (function ($, undefined) {
            
                console.log(this);
            
            }).call(window.myNamespace = window.myNamespace || {}, jQuery);
            

            【讨论】:

              【解决方案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/

              【讨论】:

                【解决方案18】:

                我的习惯是使用function 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: Creating Object with Object.create()

                【讨论】:

                  【解决方案19】:

                  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
                  

                  【讨论】:

                    【解决方案20】:

                    我们可以这样独立使用:

                    var A = A|| {};
                    A.B = {};
                    
                    A.B = {
                        itemOne: null,
                        itemTwo: null,
                    };
                    
                    A.B.itemOne = function () {
                        //..
                    }
                    
                    A.B.itemTwo = function () {
                        //..
                    }
                    

                    【讨论】:

                      【解决方案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。

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

                      【讨论】:

                      • 嗨,你如何从你的 sn-p 调用公共函数?我试过namespace.a();
                      • @olivier 是的,就是这样。虽然现在使用 ES6,但我通常使用对象字面量的简写语法 (ponyfoo.com/articles/es6-object-literal-features-in-depth)
                      • 我只想强调函数定义末尾的()集合。它们是必需的,很容易错过它们。我遇到了与@olimart 相同的问题,并通过添加它们解决了它。
                      【解决方案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]];
                       }
                      }
                      

                      要注册命名空间,只需调用上述函数,参数为命名空间,以'.'(点)分隔。 例如 让您的应用程序名称为 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。

                      希望对你有帮助。

                      【讨论】:

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

                        【讨论】:

                          【解决方案24】:

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

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

                          在 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 模式。

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

                          优势

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

                          缺点

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

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

                          【讨论】:

                            【解决方案25】:

                            如果你需要私有作用域:

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

                            【讨论】:

                              【解决方案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);

                              【讨论】:

                                【解决方案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
                                

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

                                【讨论】:

                                  【解决方案28】:

                                  ES6 Modules Namespace imports

                                  // 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 的成员提供,从而有效地赋予它自己的命名空间。

                                  【讨论】:

                                    【解决方案29】:

                                    JavaScript 还没有命名空间的原生表示,但 TypeScript 有。

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

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

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

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

                                    延伸阅读:

                                    【讨论】:

                                      猜你喜欢
                                      • 2018-07-13
                                      • 2011-01-31
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2011-07-22
                                      • 2011-03-30
                                      • 1970-01-01
                                      • 2011-01-06
                                      相关资源
                                      最近更新 更多