【问题标题】:Defining an implementation independent version of the global object in JavaScript在 JavaScript 中定义全局对象的独立于实现的版本
【发布时间】:2012-01-06 23:38:27
【问题描述】:

我正在尝试在 JavaScript 中定义 global 对象,如下所示:

var global = this.global || this;

上面的语句是在全局范围内的。因此,在浏览器中,this 指针是window 对象的别名。假设它是在当前网页上下文中执行的第一行 JavaScript,那么global 的值将始终与this 指针或window 对象的值相同。

在 CommonJS 实现中,例如 RingoJS 和 node.js,this 指针指向当前的ModuleScope。但是,我们可以通过ModuleScope 上定义的属性global 访问global 对象。因此我们可以通过this.global 属性访问它。

因此,这段代码 sn-p 适用于所有浏览器,至少适用于 RingoJS 和 node.js,但我还没有测试过其他 CommomJS 实现。因此,我想知道这段代码在任何其他 CommonJS 实现上运行时是否不会产生正确的结果,如果是,我该如何修复它。

最后,我打算在 lambda 表达式中为我的实现独立的 JavaScript 框架使用它,如下所示(来自 jQuery 的想法):

(function (global) {
    // javascript framework
})(this.global || this);

【问题讨论】:

    标签: javascript jquery node.js global commonjs


    【解决方案1】:

    this 与范围无关。

    (function(){
        (function(){
            (function(){
    
                (function(){
                alert( this ); //global object
                })()
    
            }).bind({})()
        }).apply({})
    }).call({})
    

    this 仅在函数调用期间解析,归结为几个简单的规则。

    1. 如果函数作为某个对象的属性调用,则该对象将在函数内部为this
    2. 如果按原样调用函数,this 将未定义,因此在非严格模式下它将是全局对象
    3. 如果函数使用.call/.apply 调用,那么this 由您自己显式设置。

    如您所见,它属于规则#2,它解析为undefined。而且由于没有"use strict";

    将 ThisBinding 设置为全局对象

    编辑:我现在在 RingoJS 中进行了一些快速测试,他们实际上将“全局对象”放入了实际的全局对象(由标准定义),即 ModuleScope。仅仅因为大多数 js 实现中的实际全局对象都有 Object 和 String 等,如果它下面也有这些对象,则不会使对象成为全局对象。在 RingoJS 中可以访问 StringObject 的原因是因为它们将它们放入了 ModuleScope 原型中:

    var logs = require('ringo/logging').getLogger("h");
    
    logs.info( Object.getPrototypeOf( this ) === this.global );
    //true
    

    进一步证明ModuleScope 是实际的全局对象:

    this.property = "value";
    logs.info( property );
    //"value"
    

    所以这种诡计没有任何收获,它不能解决任何问题:

    function injectGlobal(){
    globalProperty = "value"; // "use strict" would fix this!
    }
    
    injectGlobal()
    
    logs.info( globalProperty );
    //"value"
    

    吐槽,this 已经根据本文前面给出的规则指代了实际的全局对象。 this.global 不是标准定义的真正的全局对象,它只是一个容器。

    此外,您可以在浏览器中模拟这种行为:

    考虑 scopehack.js

    this.global = window.global || top.global || {};
    

    考虑 main.html:

    <script src="scopehack.js"></script>
    <script>
    this.global.helloWorld = "helloWorld"; //"global scope"
    this.helloWorld = "helloWorld" //"ModuleScope"
    </script>
    
    <iframe src="module.html"></iframe>
    

    最后是一个“模块”module.html:

    <script src="scopehack.js"></script>
    <script>
        with( this.global ) { //poor mans RhinoJS scope injection, doesn't work for writing
            console.log( helloWorld ); //"global scope" - "helloWorld"
            console.log( this.helloWorld ); //"ModuleScope" undefined
        }
    </script>
    

    module.html 和 main.html 中哪个是实际的全局对象?它仍然是this

    TLDR:

    var obj = {
    "String": String,
    "Object": Object,
    .....
    };
    

    不使 obj 成为全局对象。

    【讨论】:

    • 这很好,但我已经知道了。这就是我在代码中使用 this 而不是 window 的原因 - 它适用于所有 JavaScript 实现。然而,在 RingoJS 和 node.js 中,this 指针并不指向global 对象。它指向ModuleScope。因此我不得不写this.global || this。这不是我所期望的答案。对不起。 =(
    • @AaditMShah,对。 this.globalthis 一样好,因为如果global 是一个全局变量,那么this 也必须是全局变量,this.global 才能工作。你可以在任何地方使用global,除非它在本地被覆盖,否则它将解析为全局对象......我不太明白你的问题是什么:(
    • 顺便说一句,当我说“上述语句在全局范围内。因此在浏览器中,this 指针是window 对象的别名。”,我试图传达这一点它没有写在函数内部,因此它只能是window 对象(规则#1 和#3 不适用)。就我而言,规则 #2 也部分不正确,因为 this 指针不在函数内。当我们在全局上下文中使用 this 时,它在浏览器中始终是 window 对象,仅此而已。
    • @Esailija - 真的很难解释。看,this.global 是一个指向 global 对象的局部变量,this.global !== this 将返回 true 因为在 RingoJS 和 node.js 中,this 指针永远不会指向 global 对象(就像你规则#2)。相反,它指向ModuleScope。在ModuleScope 上有一个名为global 的属性,它指向global 对象。无法像在浏览器环境中那样直接访问 global 对象。
    • @AaditMShah,在模块内你可以尝试做:var a = (function(){return this;})(); a 仍然是模块对象吗?根据 javascript 规范,它应该是未定义的或全局的。
    【解决方案2】:

    实现独立版本并非易事

    (function (global) {
        // javascript framework
    })(
       this && this.global || // ringoJS
       typeof root !== "undefined" && root || // node.js
       typeof global !== "undefined" && global || // more node.js
       typeof GLOBAL !== "undefined" && GLOBAL || // more node.js
       typeof window !== "undefined" && window || // browsers
       this // either undefined or some global default?
    );
    

    您将不得不对每个环境的特征检测进行硬编码。

    【讨论】:

    • root 是 Node 中的实际全局对象。
    • @AaditMShah 不,不是。 this || this.global 将返回 this 而不是 this.global
    • root === GLOBAL 应该始终为真。 root === global 将取决于您当前所处的上下文。例如,在 REPL 中,如果您在全局上下文中运行,那么它是正确的,否则它不是。
    • 这实际上是一个非常重要的区别,因为它改变了当你做扩展原生原型之类的事情时发生的事情。如果您不在全局上下文中,root.Object 将返回 undefined,但如果您在,那么它将指向包含所有 Node 对象继承的 Object.prototypeObjectthis 也可以指向完全任意的东西,而不是 rootglobalGLOBAL,或者它可以指向 root
    • 据我所知,这在任何地方都没有记录,但是 Node 的大部分代码是用 JavaScript 实现的,所以它很容易上手。相关的代码位是github.com/joyent/node/blob/master/lib/module.js#L394github.com/joyent/node/blob/master/src/node.js#L125github.com/joyent/node/blob/master/src/node_script.cc#L112
    【解决方案3】:

    阅读 Esailija 和 Raynos 的回答后,我了解到我的代码 this.global || this 不适用于 node.js 中的所有情况;如果全局范围内已经存在名为 global 的变量,它甚至可能在浏览器中失败。

    Esailija 指出this.global 并不是真正的global 对象,而是指出this 是RingoJS 中的global 对象;虽然我理解他的论点,但出于我的目的,我需要this.global 而不是this

    Raynos 建议我为每个 CommonJS 环境硬编码特征检测。但是由于我目前只支持 RingoJS 和 node.js,我只需要测试 globalwindow。因此我决定坚持使用this.global || this

    尽管如此,正如我之前所说,this.global || this 并不适用于 node.js 中的所有情况,正如我从 benvie 的 cmets 中了解到的那样。在 node.js REPL 中,我意识到我需要 this 而不是 this.global。但是,this.global || this 表示 this.global。在 node.js 模块中,我需要 this.global 而不是 this。但是,它表示this,因为this.globalundefined。因此,为了解决这个问题,我最终决定使用以下代码:

    (function (global) {
        // javascript framework
    })(typeof global !== "undefined" && global || this);
    

    我使用此代码的原因是因为在 node.js 模块中 this.globalundefined。因此我们必须直接使用global。因此我们使用typeof global !== "undefined" &amp;&amp; global 来获取RingoJS 和node.js 中的global 对象;我们使用 this 作为浏览器中的 global 对象 (window) 并作为默认后备。

    注意:我没有提供在 node.js REPL 中查找 global 对象的任何逻辑,因为我不相信我的框架会直接在 REPL 中使用。然而,一旦理解了在 node.js 中查找global 对象的复杂性,正如本维指出的那样,编写查找它的逻辑应该是相当简单的。我知道我没有。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-20
      • 1970-01-01
      • 2018-08-06
      • 2020-10-26
      • 2014-10-14
      • 1970-01-01
      相关资源
      最近更新 更多