【问题标题】:JavaScript NamespaceJavaScript 命名空间
【发布时间】:2011-03-25 13:40:26
【问题描述】:

我想为我的应用程序创建一个全局命名空间,并且在该命名空间中我想要其他命名空间:

例如

Dashboard.Ajax.Post()

Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent();

我还想将它们放在单独的文件中:

  • Ajax.js
  • RetrieveContent.js

但是我尝试过使用 this 方法,但是它不起作用,因为在 2 个不同的地方为命名空间使用了相同的变量名。谁能提供替代方案?

谢谢。

【问题讨论】:

  • 以不同的方式命名您的命名空间?
  • 我想这是一种选择,但我希望将所有内容都包含在一个命名空间下,因为我认为这样会更有序。

标签: javascript design-patterns namespaces


【解决方案1】:

您只需要确保在您的命名空间对象已经创建的情况下不要踩踏它。这样的事情会起作用:

(function() {
    // private vars can go in here


    Dashboard = Dashboard || {};
    Dashboard.Ajax = {
        Post: function() {
            ...
        }
    };
})();

RetrieveContent 文件的定义也类似。

【讨论】:

  • 嗨,我也想把我的代码放在命名空间中,但我有一个问题:我有很多 JavaScript 文件,是否应该添加 Dashboard = Dashboard || {};(如果那是我的命名空间)在每个文件?其次,我是否必须将每个变量都添加到 Dashboard.x 而不是 x 的前缀,或者这不是必需的,因为所有内容都位于同一个命名空间中?
【解决方案2】:

Here 是一篇非常好的关于 JavaScript 中各种“模块模式”的文章。关于如何扩充模块或命名空间以及维护跨文件私有状态有一个非常不错的小节。也就是说,单独文件中的代码会依次执行,并在执行后适当地扩充命名空间。

我还没有彻底探索过这种技术,所以没有任何承诺......但这是基本的想法。

dashboard.js

(function(window){

    var dashboard  = (function () { 
        var my = {}, 
            privateVariable = 1; 

        function privateMethod() { 
            // ... 
        } 

        my.moduleProperty = 1; 
        my.moduleMethod = function () { 
            // ... 
        }; 

        return my; 
    }());

    window.Dashboard = dashboard;
})(window);

dashboard.ajax.js

var dashboard = (function (my) { 
    var _private = my._private = my._private || {}, 
        _seal = my._seal = my._seal || function () { 
            delete my._private; 
            delete my._seal; 
            delete my._unseal; 
        }, 
        _unseal = my._unseal = my._unseal || function () { 
            my._private = _private; 
            my._seal = _seal; 
            my._unseal = _unseal; 
        }; 

    // permanent access to _private, _seal, and _unseal

    my.ajax = function(){ 
        // ...
    }

    return my; 
}(dashboard || {}));

dashboard.retrieveContent.js

var dashboard = (function (my) { 
    var _private = my._private = my._private || {}, 
        _seal = my._seal = my._seal || function () { 
            delete my._private; 
            delete my._seal; 
            delete my._unseal; 
        }, 
        _unseal = my._unseal = my._unseal || function () { 
            my._private = _private; 
            my._seal = _seal; 
            my._unseal = _unseal; 
        }; 

    // permanent access to _private, _seal, and _unseal

    my.retrieveContent = function(){ 
        // ...
    }

    return my; 
}(dashboard || {}));

【讨论】:

  • 您能否向我解释一下这如何允许访问在另一个文件中声明的私有变量?如果我要调用dashboard._seal() 更具体,dashboard._unseal() 如何让我再次访问私有?
【解决方案3】:

Yahoo Namespace 函数正是针对这个问题而设计的。

添加:

函数的source 可用。如果需要,您可以将其复制到自己的代码中,将根目录从 YAHOO 更改为其他内容,等等。

【讨论】:

    【解决方案4】:

    如果您想使用或检查预烘焙(即经过测试)的解决方案,有几个库已经提供了此类功能。

    最简单且最无错误的方法可能是jQuery.extend,将deep 参数设置为true。 (我说它没有错误的原因并不是因为我认为jQuery.extend 比其他任何库都更少的错误——而是因为它提供了一个明确的选项,可以将属性从发送方深复制到接收方——这大多数其他库明确不提供。这将防止以后在您的程序中出现许多难以诊断的错误,因为您使用了浅拷贝extend,现在有函数在您不期望它们在其中执行的上下文。(但是,如果您知道在设计方法时将如何扩展基础库,那么这应该不是问题。)

    【讨论】:

      【解决方案5】:

      创建 NS 对象后,您应该可以从任何地方添加到它。虽然您可能想尝试var NS = NS || {}; 以确保 NS 对象存在且未被覆盖。

      // NS is a global variable for a namespace for the app's code
      var NS = NS || {};
      
      NS.Obj = (function() {
      
        // Private vars and methods always available to returned object via closure
        var foo; // ...
      
        // Methods in here are public
        return {
          method: function() {
      
          }
        };
      
      }());
      

      【讨论】:

        【解决方案6】:

        你可以做这样的事情......

        使用命名空间库的 HTML 页面:

        <html>
        <head>
            <title>javascript namespacing</title>
            <script src="dashboard.js" type="text/javascript"></script>
            <script src="ajax.js" type="text/javascript"></script>
            <script src="retrieve_content.js" type="text/javascript"></script>
            <script type="text/javascript">
                alert(Dashboard.Ajax.Post());
                alert(Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent());
                Dashboard.RetrieveContent.Settings.Timeout = 1500;
                alert(Dashboard.RetrieveContent.Settings.Timeout);
            </script>
        </head>
        
        <body>
            whatever...
        </body>
        
        </html>
        

        Dashboard.js:

        (function(window, undefined){
            var dashboard = {};
            window.Dashboard = dashboard;
        })(window);
        

        Ajax.js:

        (function(){
            var ajax = {};
            ajax.Post = function() { return "Posted!" };
            window.Dashboard.Ajax = ajax
        })();
        

        Retrieve_Content.js:

        (function(){
            var retrieveContent = {};
            retrieveContent.RefreshSalespersonPerformanceContent = function() { 
                return "content retrieved"
            };
        
        
            var _contentType;
            var _timeout;
            retrieveContent.Settings = {
                "ContentType": function(contentType) { _contentType = contentType; },
                "ContentType": function() { return _contentType; },
                "Timeout": function(timeout) { _timeout = timeout; },
                "Timeout": function() { return _timeout; }
            };
        
            window.Dashboard.RetrieveContent = retrieveContent;
        
        })();
        

        Dashboard.js 充当其下所有命名空间的起点。其余的在各自的文件中定义。在 Retrieve_Content.js 中,我在 Settings 下添加了一些额外的属性,以便在需要时了解如何执行此操作。

        【讨论】:

        • 虽然不能保证retrieve_content.js 会在Dashboard.js 之后被加载和解析。如果在加载 Dashboard.js 之前加载了任何依赖库,则分配将失败。
        • 一般Dashboard.js 将首先被加载和解析,但是是的,这不能保证。 Dashboard 对象可以在分配之前检查并在必要时创建,但这需要在retrieve_content.jsajax.js 中重复一些代码。 OP 的单独文件要求使我想到了上述内容。
        【解决方案7】:

        我相信模块模式可能正适合您。这是一篇关于不同模块模式的好文章。

        http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

        【讨论】:

        • 只有链接的答案没有那么有用。这里应该有足够的解释。问题是如何从两个不同的位置定义命名空间中的对象
        • @JuanMendes:这篇文章准确地解释了这一点。几个热门答案基本上也只是链接。而且,这是一年半前的事了。
        • 我从来没有真正找到适合命名空间的模块模式。命名空间不是“可重用”代码块(尽管 NS 的各个部分可能是)。对象字面量对我来说一直很好用。
        【解决方案8】:

        我强烈建议您使用这种技术:

        https://github.com/mckoss/namespace

          namespace.lookup('com.mydomain.mymodule').define(function (ns) {
          var external = namespace.lookup('com.domain.external-module');
        
          function myFunction() {
            ...
          }
        
          ...
        
          ns.extend({
            'myFunction': myFunction,
             ...
          });
        });
        

        我已经使用这种模式几年了;我希望更多的图书馆能做同样的事情;它也让我可以更轻松地在我的不同项目之间共享代码。

        【讨论】:

          【解决方案9】:

          我编写了这个函数来简化命名空间的创建。也许它会帮助你。

          function ns(nsstr) {
              var t = nsstr.split('.');
              var obj = window[t[0]] = window[t[0]] || {};
              for (var i = 1; i < t.length; i++) {
                  obj[t[i]] = obj[t[i]] || {};
                  obj = obj[t[i]];
              }
          }
          
          ns('mynamespace.isawesome.andgreat.andstuff');
          mynamespace.isawesome.andgreat.andstuff = 3;
          
          console.log(mynamespace.isawesome.andgreat.andstuff);
          

          【讨论】:

            【解决方案10】:

            bob.js 可以帮助定义您的命名空间(以及其他):

            bob.ns.setNs('Dashboard.Ajax', {
            
                Post: function () { /*...*/ }
            });
            
            bob.ns.setNs('Dashboard.RetrieveContent', {
            
                RefreshSalespersonPerformanceContent: function () { /*...*/ }
            });
            

            【讨论】:

              【解决方案11】:

              实施:

              namespace = function(packageName)
              {
                  // Local variables.
                  var layers, layer, currentLayer, i;
              
                  // Split the given string into an array.
                  // Each element represents a namespace layer.
                  layers = packageName.split('.');
              
                  // If the top layer does not exist in the global namespace.
                  if (eval("typeof " + layers[0]) === 'undefined')
                  {
                      // Define the top layer in the global namesapce.
                      eval(layers[0] + " = {};");
                  }
              
                  // Assign the top layer to 'currentLayer'.
                  eval("currentLayer = " + layers[0] + ";");
              
                  for (i = 1; i < layers.length; ++i)
                  {
                      // A layer name.
                      layer = layers[i];
              
                      // If the layer does not exist under the current layer.
                      if (!(layer in currentLayer))
                      {
                          // Add the layer under the current layer.
                          currentLayer[layer] = {};
                      }
              
                      // Down to the next layer.
                      currentLayer = currentLayer[layer];
                  }
              
                  // Return the hash object that represents the last layer.
                  return currentLayer;
              };
              


              结果:

              namespace('Dashboard.Ajax').Post = function() {
                  ......
              };
              
              namespace('Dashboard.RetrieveContent').RefreshSalespersonPerformanceContent = function() {
                  ......
              };
              


              要点:

              namespace.js

              【讨论】:

                猜你喜欢
                • 2010-12-05
                • 2011-12-02
                • 1970-01-01
                • 2011-01-31
                • 2013-03-10
                • 2011-12-05
                • 2015-11-15
                • 2013-09-12
                相关资源
                最近更新 更多