【问题标题】:Javascript namespacing only works with varJavascript 命名空间仅适用于 var
【发布时间】:2012-09-16 06:39:39
【问题描述】:

我有以下代码:

(function(){
...
var n = n || {};
...
})(); 

这是我想要的,将 n 设置为 {},因为这是第一次遇到它并且将是未定义的。不幸的是,由于 this 在函数内部,n 的范围仅限于该函数,我无法在其他脚本中使用它。

我只想将行更改为n = n || {},但出现错误:ReferenceError: n is not defined

将其更改为 n = {} 按预期工作;然而,这不是我想要的。我不明白为什么当我不使用varn 的不确定性会导致错误,并且当我使用时会按预期工作(作为用于获取OR 语句右侧的错误值)。根据我对 var 关键字的理解,我希望它是两者或两者都不是。

为什么var 很重要,这是怎么回事?

【问题讨论】:

    标签: javascript variables namespaces scope var


    【解决方案1】:

    1) 在第一轮评估中,var n = n || {}; 你正在创建变量:

    var n = undefined;
    

    2) 在第二轮评估中,您正在有条件地设置值:

    n = undefined || {};
    

    3) 如果没有 var 语句,您将得到一个根本不存在的 var 被引用

    var myVar = non_existant_var; // error
    

    如果 var 已经存在,比如说,在全局范围内,或者当前函数嵌套在其中的任何其他函数中,它将使用它找到的任何n,在链上的路上。如果不存在,它将适合。

    因此,与其故意在代码中挖洞,不如让代码知道您要访问的范围,为什么不直接访问呢?

    (function (name) {
        window[name] = window[name] || {}; // returns undefined and not reference error
        var ns = window[name];
    
        ns.init = function () {};
        // .............
        /* Don't return the application to a var -- it already exists in the global scope */
    }("BigLongApplicationName"));
    
    BigLongApplicationName.init(); // === window.BigLongApplicationName.init();
    

    【讨论】:

      【解决方案2】:

      所以听起来您在创建全局变量时遇到了麻烦。

      很好。

      在 JavaScript 中创建全局变量比它应该的要容易。

      您的问题是 n 没有定义(duh)。您不能引用尚未定义的变量。但你可能在想,“这与使用 var 的版本有何不同???”这是个好问题。

      JavaScript 做了一种叫做“提升”变量定义的事情。如果您在函数中定义变量,则该定义会隐式重定位到函数的顶部。所以当你写的时候:

      var n = n || {};
      

      真正发生的事情更接近于:

      var n;
      n = n || {};
      

      然而,事情并不是那么简单,因为如果您尝试编写该代码,您最终会发现n 总是被设置为{}。但是一般的本质是存在的。变量声明发生在赋值之前。

      如果您删除var,它将导致引用错误,因为没有声明可以提升。因此,一种“适当的”(我松散地使用该术语,因为创建全局变量从来都不是“适当的”)做你想做的事情的方法是将你的var 放在你的函数包装器之外。像这样:

      var n = n || {};
      (function () {
          //do stuff.
      }()); 
      

      很遗憾,你不能这样做:

      var n; 
      (function () {
          n = n || {};
      }());
      

      这与我上面的示例具有相同的“问题”。如果在别处定义了n,它会被var n;设置为undefined,然后在函数中设置为{}。通过在函数之外进行整个声明和赋值,您将获得所需的内容。我假设(根据您的问题的标题)实际上是命名空间,而不是一些任意的全局变量。那会很淘气! ;-)

      更新:

      哦,顺便说一句,更好的方法可能是显式引用全局对象:

      (function (exports) {
          exports.n = exports.n || {};
      }(this));
      

      这可能会更好地与诸如 node.js 之类的东西以及任何可能包装您的代码的东西(例如 $(function() { }))一起使用。

      【讨论】:

        【解决方案3】:

        1) 小的改变会有所帮助:

        (function() {
            var n = window.n = window.n || {};
            // Code of your module...
            n.hello = function() {
                alert('Hello');
            };
        })();
        
        n.hello();
        

        DEMO

        通过这种方式,您拥有全局可见的n 和对同一对象的本地引用n 在你的函数里面。

        2) 我还建议你用下一种方式重新设计它。

        使每个模块像:

        var MyApplication = (function(app) {
        
            // Code of the module here...
        
            app.hello = function() {
                console.log('Hello!');
            };
        
            return app;
        })(MyApplication || {});
        
        MyApplication.hello();
        

        【讨论】:

          【解决方案4】:

          您已经遇到了未定义变量和具有未定义值的已定义变量之间的区别。

          引用未定义的变量将导致异常 (ReferenceError: n is not defined)

          引用具有未定义值的已定义变量很好,并且会在条件中转换为 false

          为避免这种情况,您可以将n 引用为window 对象的属性,因为属性永远不能未定义,只能具有未定义的值。

          (function(){
          ...
          window.n = window.n || {};
          ...
          })(); 
          

          【讨论】:

          • 那么为什么当我使用 var n 时已经定义了变量,而当我使用 n 时却没有?是不是因为它是在变量对象上创建的,所以新属性是在引用之前创建的?
          【解决方案5】:

          n 还没有被声明,所以检查n 是否为真是行不通的。您可以明确检查n 是否已被声明:

          n = typeof n !== 'undefined' ? n : {};
          

          但这不是很好看。要将某些内容放在全局范围内,请使用 window.n 代替 n 或在函数外部声明 n

          var n;
          
          (function(){
          ...
          n = n || {};
          ...
          })(); 
          

          【讨论】:

            【解决方案6】:

            var 创建局部变量而不使用它会污染全局命名空间。浏览器应该防止这种情况发生,因为大多数情况下您不想创建全局变量。

            你可以做的是使用window对象:

            window.n = window.n || {}
            

            但这仅适用于浏览器环境。如果不是这种情况(例如,您使用的是服务器端 JavaScript),则以这种方式检索全局对象:

            var global = (function getGlobal(){
                return (function(){
                    return this;
                }).call(null);
            }());
            

            然后:

            global.n = global.n || {};
            

            【讨论】:

              【解决方案7】:

              您可以在代码前添加var n;,您将拥有全局nvar 很重要,因为它使 n 成为函数的局部变量。

              【讨论】:

                【解决方案8】:

                在严格模式下,您需要显式使用全局命名空间,通常为window;更改线路以使用 window.n 应该可以得到你想要的:

                window.n = window.n || {};
                

                【讨论】:

                • 这仅适用于浏览器,不适用于服务器端。作者没有具体说明。看我的回答:stackoverflow.com/questions/12571210/…
                • @Miszy,我指定它需要全局命名空间,并且在非浏览器环境中工作的任何作者都应该知道该对象的名称。
                【解决方案9】:

                在函数的最后做:

                window.n = n;
                

                这会将 n 放在全局命名空间中,允许您在其他脚本中使用它。

                【讨论】:

                  猜你喜欢
                  • 2011-07-24
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-01-22
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-03-30
                  • 2014-01-24
                  相关资源
                  最近更新 更多