在JavaScript中定义全局对象的独立于实现的版本

Defining an implementation independent version of the global object in JavaScript

本文关键字:实现 版本 于实现 独立 定义 全局 对象 JavaScript      更新时间:2023-09-26

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

var global = this.global || this;

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

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

因此,这段代码片段在所有浏览器中工作,至少在ringgojs和node.js中,但我没有测试其他commjs实现。因此,我想知道如果这段代码不会产生正确的结果时,运行在任何其他CommonJS实现,如果是这样,我如何修复它。

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

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

this与scope无关。

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

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

    如果函数作为某个对象的属性被调用,那么该对象将在函数内部的this
  1. 如果函数按原样调用,this将是未定义的,因此在非严格模式下,它将是全局对象
  2. 如果使用.call/.apply调用函数,则this由您自己显式设置。
因此,正如您所看到的,它将属于规则#2,解析为undefined。由于没有"use strict";:

将ThisBinding设置为全局对象

编辑:我现在在ringgojs中运行了一些快速测试,他们实际上把"全局对象"放在实际的全局对象(按标准定义)中,这是ModuleScope。仅仅因为在大多数js实现中实际的全局对象有object和String等等,如果对象下也有这些对象,就不会使对象全局。你可以在ringgojs中访问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" 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成为全局对象。

实现独立版本不是小事

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

你必须在每个环境下硬编码特征检测

在阅读了Esailija和Raynos的答案后,我明白了我的代码this.global || this将不适用于node.js中的所有情况;如果一个名为global的变量已经存在于全局作用域中,它甚至可能在浏览器中失败。

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

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

然而,正如我之前所说的,this.global || this并不适用于node.js中的所有情况,正如我从benvie的评论中所理解的那样。在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" && global在ringgojs和node.js中获得global对象;我们使用this作为浏览器中的global对象(window),并作为默认的回退。

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