在严格模式下在未知环境中获取对全局对象的引用

IT技术 javascript global ecmascript-5 ecma262 strict-mode
2021-02-24 20:10:10

在未知主机环境中以 ES5 严格模式获取全局对象句柄的推荐方法是什么

ECMAScript 不提供引用我所知道的全局对象的内置方法。如果是这样,这就是我正在寻找的答案。

已知环境中,全局对象通常具有自引用属性。由于全局对象是全局作用域VO,全局对象的属性是全局变量,所以我们可以使用它们从任何地方获取全局对象的句柄:

  • 在 Web 浏览器中,我们可以使用windowself

  • 在 node.js 中,我们可以使用global.

但是,并非所有主机环境都必须如此。据我所知,Windows Script Host 没有提供任何访问全局对象的方法。在 WSH 中获取全局对象的推荐方法似乎是this在它不能解析为对象的上下文中使用关键字。例如:

var GLOBAL = (function(){return this}());

这种技术适用于任何宿主环境,但不适用于严格模式,因为 undefinedthis不会在严格模式下引用全局对象

如果在严格模式代码中计算 this,则 this 值不会被强制转换为对象。null 或 undefined 的 this 值不会转换为全局对象,并且原始值不会转换为包装器对象。通过函数调用(包括使用 Function.prototype.apply 和 Function.prototype.call 进行的调用)传递的 this 值不会强制将传递的 this 值传递给对象 (10.4.3, 11.1.1, 15.3.4.3, 15.3. 4.4)。

正如预期的那样,以下代码导致undefined

(function(){
    "use strict";
    var GLOBAL = (function(){return this}());
    console.log(GLOBAL);
}());

那么,无论严格模式如何,在任何环境中获取全局对象句柄的正确方法是什么

顺便说一句,我目前的方法是嗅探引用全局对象的全局变量,如下所示:

var self, window, global = global || window || self;

...然后只需使用global. 我认为这是一个糟糕的解决方案,原因有很多,其中大部分是相当明显的,而且它没有解决 WSH 问题。

4个回答

在 ES5 中,您可以通过间接 eval 调用从严格模式中获取对全局对象的引用:

"use strict";
var global = (1,eval)('this');

看看我的文章特别是关于严格模式的这一

感谢您的客气话 :) 说到 eval 技巧,我听说 Node.js 在这方面没有遵循 ES5 的行为,并且在幕后还有其他一些魔法。从来没有机会测试它,所以你可能想仔细检查一下。不过,它确实适用于我尝试过的所有浏览器,而且理论上应该可以使用,因为 ES5 中是这样规定的。
2021-04-20 20:10:10
我会测试它的节点......如果在节点坏了,我就可以把它检查已经存在的global工作围绕着(和重命名global的例子,别的东西,所以它不会影现行global如果从函数作用域调用)。这正是我要找的,谢谢。
2021-04-24 20:10:10
嘿,我只是有一个想法。我可以获得对全局对象的[[Prototype]]的引用,然后简单地执行诸如constructor.prototype.foo = 'bar'. 这应该足以设置全局对象的属性,尽管它显然不能很好地获取属性。你怎么认为?这不会在控制台中工作,除非你eval顺便说一下,看起来像控制台 shadows constructor我认为我们也可以使用这个技巧来确保我们拥有真实的eval而不是被覆盖的。
2021-05-07 20:10:10
感谢您的回复,我已经阅读了您的许多文章(顺便说一句,这些文章很棒),但我一定错过了这一篇。我想过使用eval,但不知道这个间接技巧(eval显然,只是在函数范围内不使用间接评估)。
2021-05-13 20:10:10
2021-05-13 20:10:10

全局代码中thisBinding无论严格模式如何,都设置为全局对象。这意味着您可以将它从那里传递到您的module IEFE:

// "use strict"; or not
(function(global) {
    "use strict";
    …
    console.log(global);
    …
}(this));
实际上它类似于@DavidEllis 的回答,但不会污染全局范围:-)
2021-04-27 20:10:10

在严格模式下,获取全局对象引用的方法是在引用自身的全局对象中分配一个变量。

this意味着在全局上下文中的全局对象,所以解决方案很简单:

"use strict";
var global = global || this;
(function() { global.hello = "world"; })();
console.log(hello); // Outputs 'world' as expected

确实意味着您必须使用对自身的引用来污染全局名称空间,但就像您说的那样,它应该已经存在了。

等等,global除非您使用typeof支票,否则引用不会失败吗?
2021-04-17 20:10:10
是的,当我最初发布时,我知道var吊装,但说实话,我完全没有在这个特定的背景下把它放在一起。谢谢你的提醒:)
2021-04-17 20:10:10
@wilmoore:不,因为var声明被挂起。var global = global || this;被有效地处理为var global; global = global || this;如果global已经存在,var global则为空操作,否则创建时的初始值为undefined,然后在下一条语句中覆盖。
2021-04-30 20:10:10

Mathias Bynens 有一篇关于这个主题的优秀文章。以下适用于所有环境,use strict或者不适用,包括启用 CSP 的环境,例如 Chrome 扩展程序。

(function() {

    // A globalThis polyfill | # | Adapted from https://mathiasbynens.be/notes/globalthis

    if (typeof window !== 'undefined' && window && window.window === window) { return window } // all browsers
    else { // webworkers, or server-side Javascript, like Node.js
        try {
            Object.defineProperty( Object.prototype, '__magic__', { // hocus pocus
                get: function() {
                    return this;
                },
                configurable: true // This makes it possible to 'delete' the getter later
            });
            __magic__.globalThis = __magic__;
            delete Object.prototype.__magic__;

            return globalThis;
        } catch (e) {
            // we shouldn't ever get here, since all server-side JS environments that I know of support Object.defineProperty
            return (typeof globalThis === 'object') ? globalThis : ( (typeof global === 'object') ? global : this );
        }
    }
})();