【问题标题】:How can I define a global variable only if it doesn't exist in ES5 strict?仅当 ES5 严格中不存在全局变量时,如何定义它?
【发布时间】:2012-10-02 16:57:27
【问题描述】:

我正在 ES5 中编写 ES Harmony Symbol/Name 的实现。我将使用名称Symbol,但我希望浏览器使用任何预先存在的Symbol,以防它已经存在(在未来的浏览器中)。我希望我的代码严格符合 ES5 标准并且可移植到其他项目。

这是在非严格的 ES3/ES5 中做我想做的事情的一种方法:

(function() {

    // If Symbol already exists, we're done.
    if(typeof Symbol != 'undefined') return;

    // This becomes global because it wasn't declared with var
    Symbol = function() {
        // ...
    };

})();

但是,它不符合 ES5 严格标准,因为 Symbol 没有明确定义。

实现此目的的其他方法包括访问 window 对象 (window.Symbol = ...),但这也不好,因为我不希望我的代码假定它在浏览器环境中运行。

在 ES5 strict 中如何做到这一点?

【问题讨论】:

    标签: javascript ecmascript-5


    【解决方案1】:

    其他用户发布的答案将我引向了一个类似的 StackOverflow 问题,该问题为我提供了在 Google 中搜索以找到答案的正确术语。解决办法:

    我最终能够使用indirect eval 解决这个问题,描述为here

    使用间接评估,在上面链接的文章中详细介绍,按照 ES5 规范在全局范围内执行代码。我选择采用这种方法是因为它符合 ES5 规范,并且它允许将代码从字面上删除到任何地方,甚至在包管理器的另一个函数中,仍然可以找到全局对象(提供的其他答案做不到)。

    解决方案是这样的:

    (function() {
    
        'use strict';
    
        var _global = (0, eval)('this');
    
        // If Symbol is already defined, there's nothing to do.
        if(_global.Symbol) return;
    
        _global.Symbol = function() {
            // ...
        };
    
    })();
    

    关键是使用间接eval 来检索全局对象(this 在间接eval 的上下文中)。

    这应该适用于任何符合 ES5 的环境,包括我想要的现代浏览器和非浏览器环境。

    感谢大家的帮助!

    唯一需要注意的是,为了访问全局对象,不得不以这种间接的方式(现在更糟)使用eval(这已经够糟糕了)似乎有点骇人听闻。 global 标识符或其他访问全局对象的方法是否应该不在规范中?

    【讨论】:

    【解决方案2】:

    您如何传入您希望将 Symbol 添加到的全局范围?

    (function(global){
     if(typeof global.Symbol != 'undefined') return;
    
        // This becomes global because it wasn't declared with var
        global.Symbol = function() {
            // ...
        };
    
    })(window);  
    

    这会将它添加到窗口,但可能是其他范围或变量。

    【讨论】:

    • 我写了“全局范围”,但不一定是全局变量,这取决于调用包装器的位置/方式。
    • 我考虑过,这可能是我最好的选择,但它并不理想,因为它需要为非浏览器环境编辑文件。
    • 我对 AMD(requirejs 使用的模型)非常熟悉,因为我自己为工作编写了一个实现。这如何解决我的问题?你的意思是使用出口的能力?我想写一些可以从字面上扔到任何地方的东西。我不想依赖模块模型。
    • 是的,我的意思是能够使用导出。您希望 Symbol 在某处被定义并在其他地方被挑选​​和使用。创建全局变量可能是不好的做法,所以如果你同意遵循一些模式/实现,比如 requirejs,那么至少有一个记录的、半标准的方式你正在遵循,所以其他可能需要维护代码的人不需要面条通过意大利面条代码。
    • 但是我真的不想定义一个全局变量。我想为 ES6 中可用的东西提供一个 shim,以便其他库今天可以使用它。在 ES6 中,他们不必require 它。作为垫片,它不应该是required。
    【解决方案3】:
    'use strict';
    
    var Symbol = 1; // try to comment this line and run the script again
    
    var Symbol = (function(Symbol) {
    
    
        if(typeof Symbol != 'undefined') return Symbol;
    
        Symbol = function() {
            // ...
        };
    
        return Symbol;
    
    })(Symbol);
    
    alert(typeof Symbol);
    

    http://jsfiddle.net/f0t0n/yATJW/

    'use strict';
    
    (function(g) { // g is a global context (this passed)
    
        if(typeof g.Array != 'undefined') {
            return;
        }
    
        g.Array = function() {
            // ...
        };
    
        g.Array.prototype.foo = function() {
            console.log('bar');
        };
    })(this);
    
    console.log(this.Array);​
    

    http://jsfiddle.net/f0t0n/prwaP/

    【讨论】:

    • "实现此目的的其他方法将涉及访问窗口对象 (window.Symbol = ...),但这也不好,因为我不希望我的代码假设它在浏览器环境。”
    • 这是一个不错的尝试,但由于var 的提升,我不认为它真的有效。将Symbol 替换为确实已经存在的东西,例如Array,看看我的意思:jsfiddle.net/6jhFr
    • 这看起来很有吸引力。我有几个问题。 (1) 这真的符合 ES5-strict 标准吗?如果 this 在全局范围内运行,它会默认成为 ES5 中的全局对象吗? (2) 这是否适用于非浏览器环境(如 Node.js)? Node中全局范围内的this是指全局对象吗?这种行为是 ES5 规范的一部分吗?谢谢!
    • 否,在节点 this 中不引用 global 对象。在节点中,您可以在我的示例中明确使用global,而不是windowthis。在节点console.log(this, this === global, this === module); 将输出{} false false
    • 我安装了 Node 并进行了测试。 this === global 对我来说是 true。 +1,不过。谢谢您的帮助。最终不是我选择的解决方案,但它帮助我指出了一些好的地方。
    【解决方案4】:

    为什么需要在匿名函数中?

    // assuming global context
    if (typeof this.Symbol === 'undefined') {
        this.Symbol = function () {
            // ...
        };
    }
    

    或在函数中,按照here 的描述传递this

    (function (t) {
        if (typeof t.Symbol === 'undefined') {
            t.Symbol = function () {
                // ...
            };
        }
    })(this);
    

    【讨论】:

    • +1,谢谢。您发布的链接以及它启发的几次 Google 搜索最终将我引向了我正在寻找的答案。
    猜你喜欢
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 2018-01-27
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多