JavaScript 中如何获取全局对象?

IT技术 javascript global
2021-01-27 09:15:58

如果某个其他module已加载,我想签入脚本。

if (ModuleName) {
    // extend this module
}

但如果ModuleName不存在,那就是throw

如果我知道那Global Object什么,我可以使用它。

if (window.ModuleName) {
    // extend this module
}

但是,因为我希望我的module与浏览器和工作noderhino等等,我不能假设window

据我了解,这在 ES 5 中不起作用"use strict"

var MyGLOBAL = (function () {return this;}()); // MyGlobal becomes null

这也将失败并抛出异常

var MyGLOBAL = window || GLOBAL

所以看起来我被剩下了

try {
    // Extend ModuleName
} 
catch(ignore) {
}

这些情况都不会通过 JSLint。

我错过了什么吗?

6个回答

好吧,您可以使用typeof运算符,如果标识符不存在于作用域链的任何位置,它不会抛出 a ReferenceError,它只会返回"undefined"

if (typeof ModuleName != 'undefined') {
  //...
}

还要记住,this全局代码上值是指全局对象,这意味着如果您的if语句在全局上下文中,您可以简单地检查this.ModuleName.

关于该(function () { return this; }());技术,您是对的,在严格模式下,该this值将简单地为undefined.

在严格模式下,无论您身在何处,都有两种方法可以获取对 Global 对象的引用:

  • 通过Function构造函数:

    var global = Function('return this')();
    

使用Function构造函数创建的函数不继承调用者的严格性,只有当它们以'use strict'指令开始它们的主体时它们才是严格的,否则它们是非严格的。

此方法与任何 ES3 实现兼容。

  • 通过间接eval调用,例如:

    "use strict";
    var get = eval;
    var global = get("this");
    

以上将起作用,因为在 ES5 中,间接调用eval, 使用全局环境作为 eval 代码的变量环境和词法环境。

请参阅输入评估代码,步骤 1 的详细信息

但请注意,最后一个解决方案不适用于 ES3 实现,因为对 ES3 的间接调用eval将使用调用者的变量和词法环境作为 eval 代码本身的环境。

最后,您可能会发现检测是否支持严格模式很有用:

var isStrictSupported = (function () { "use strict"; return !this; })();
可能很明显的问题:为什么使用“use strict”然后规避它的一种影响?是否有理由认为获取全局上下文的唯一方法有点笨拙?
2021-03-24 09:15:58
+1 @CMS - 我已经数不清在本网站上阅读了您的答案多少次了。谢啦。
2021-03-26 09:15:58
@Walkerneo 一个原因可能是我们真的不应该向全局范围添加任何内容(尽管它可能很有用,例如当我们需要导出库 API 时)。另外,我认为“this”的使用受到限制(在严格模式下),因为 this 所指的内容可能并不总是很明显。实现这一点使得获得对全局对象的引用变得困难。
2021-04-07 09:15:58
可悲的是, Function('return this') 不起作用。在 Chrome 中,我得到:EvalError:拒绝将字符串评估为 JavaScript,因为在以下内容安全策略指令中,'unsafe-eval' 不是允许的脚本源:“script-src 'self''unsafe-inline'”。
2021-04-08 09:15:58
@CMS 注意Function可以在子作用域中覆盖甚至隐藏最好使用var global = (() => {}).constructor('return this')();
2021-04-10 09:15:58

2019 年更新

使用当今所有的 Webpacks 和 Broccolis、Gulps 和 Grunts、TypeScripts 和 AltScripts 以及 create-react-apps 等,这是非常无用的,但是如果您只是使用普通的、旧的、VanillaJS 并且您想要制作它同构,这可能是你最好的选择:

var global
try {
  global = Function('return this')();
} catch(e) {
  global = window;
}

即使--use_strict在节点中使用函数构造函数调用也能工作,因为函数构造函数总是在全局非严格范围内执行。

如果 Function 构造函数失败,那是因为您使用的浏览器eval被 CSP 标头禁用。

当然,随着 Deno 的进行(节点替换),他们也可能会禁止 Function 构造函数,在这种情况下,它会返回到枚举对象,例如global, module, exports, globalThisand window,然后是鸭子类型检查,这是全局的彻底...... :-/

疯狂的单行解决方案(原创):

var global = Function('return this')() || (42, eval)('this');

.

.

.

作品

  • 在每个环境中(我测试过)
  • 在严格模式下
  • 甚至在嵌套范围内

2014 年 9 月 23 日更新

如果最新浏览器中的 HTTP 标头明确禁止 eval,这现在可能会失败。

一种解决方法是尝试/捕获原始解决方案,因为只有浏览器才能运行这种类型的 JavaScript 子集。

var global;

try {
  global = Function('return this')() || (42, eval)('this');
} catch(e) {
  global = window;
}
Example:
---

    (function () {

      var global = Function('return this')() || (42, eval)('this');
      console.log(global);

      // es3 context is `global`, es5 is `null`
      (function () {
        "use strict";

        var global = Function('return this')() || (42, eval)('this');
        console.log(global);

      }());

      // es3 and es5 context is 'someNewContext'
      (function () {

        var global = Function('return this')() || (42, eval)('this');
        console.log(global);

      }).call('someNewContext');

    }());

Tested:
---

  * Chrome v12
  * Node.JS v0.4.9
  * Firefox v5
  * MSIE 8

Why:
---

In short: it's some weird quirk. See the comments below (or the post above)


In `strict mode` `this` is never the global, but also in `strict mode` `eval` operates in a separate context in which `this` *is* always the global.

In non-strict mode `this` is the current context. If there is no current context, it assumes the global. An anonymous function has no context and hence in non-strict mode assumes the global.

Sub Rant:

There's a silly misfeature of JavaScript that 99.9% of the time just confuses people called the 'comma operator'.

    var a = 0, b = 1;
    a = 0, 1;          // 1
    (a = 0), 1;        // 1
    a = (0, 1);        // 1
    a = (42, eval);    // eval
    a('this');         // the global object
您可能会通过声明a = 0, 1; // 0(a = 0), 1; // 0作为两个表达式都返回来增加逗号混淆1也许// a = 0会更好。
2021-03-20 09:15:58
嗯...我可以在测试时发誓我得到了 0...但你绝对是对的。更新了答案
2021-03-20 09:15:58
@CoolAJ86,你的新代码也适用于 ES3 实现,但如果你仔细检查它,你会发现间接eval部分根本不需要,你需要的只是var global = Function('return this')();,正如我所描述的,“用Function构造函数创建的函数不“不继承调用者的严格性”,这意味着该函数的返回值将始终是全局对象,无论实现如何-。运算符eval右侧调用||将永远不会进行,因为该函数将始终产生一个真值(全局 obj)。
2021-03-23 09:15:58
什么时候(42, eval)('this')计算表达式 换句话说,什么时候会Function('return this')()是假的?
2021-04-03 09:15:58
这不仅仅是一个奇怪的怪癖,这就是我在回答中描述为eval. 在 ES5 中,只有满足两个条件的 a 组成时,才会eval直接调用: 1. 引用的基值是环境记录。2. 引用名称为,任何其他调用方式都会导致间接调用。请小心,因为此行为在 ES3 中不起作用,因为不存在直接调用eval的概念用 ES3 实现(例如 IE8)试试这个例子CallExpressionMemberExpression"eval"eval
2021-04-11 09:15:58

为什么不简单地在全局范围内使用 this 作为包装函数的参数,如下所示?

(function (global) {
    'use strict';
    // Code
}(this));
这在 NodeJS module中不起作用,其中this === module.exports.
2021-03-29 09:15:58
令人惊讶的是,还没有人指出这并没有真正增加 Szabolcs Kurdi 的回答在一年前没有提供的任何内容。它没有解决那里的评论中提出的问题,即需要在全局范围内调用才能工作,但至少您的回答确实承认这一点。
2021-04-01 09:15:58

干得好 :)

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

这应该适用于任何地方,例如在另一个闭包中。

编辑 - 只需更仔细地阅读您的帖子,并查看有关 ES5 严格模式的部分。任何人都可以对此有所了解吗?只要我记得,这一直是获取全局对象的公认方式......我当然希望它不会最终被破坏。

编辑 2 - CMS 的回答有更多关于 ES5 严格模式对this.

@Eduardo——没有。
2021-03-27 09:15:58

我认为这在犀牛、节点、浏览器和jslint(没有额外的解决方法标志)中非常好 - 这有帮助吗?我错过了什么吗?

x = 1;
(function(global){
    "use strict";
    console.log(global.x);
}(this));

虽然我自己倾向于使用 window 对象,如果我确实需要无头测试,我可以使用 env.js (rhino) 或 Phantom (node)。

它在没有其他选项的情况下通过 jslint(如果你去掉 x=1 示例),尽管我猜这只是一个替代方案(虽然优雅是一个高度主观的因素)。
2021-03-20 09:15:58
据我所知,如果在全局范围(浏览器、犀牛、节点)中使用,它确实指的是全局范围,但我可能错了。你能展示一个例子 vm,它的工作方式不同吗?谢谢!
2021-03-21 09:15:58
您错过了this可能不引用全局对象的事实
2021-03-29 09:15:58
@SzabolcsKurdi 你是对的,'this' 在全局范围内使用时将成为全局范围。问题是不能保证函数会在全局范围内执行。例如,如果将这个 get 放在一个库中并包装在一个立即调用的函数中。我们真正要寻找的是一种获取全局作用域的方法,而不管它被调用的作用域是什么。
2021-03-30 09:15:58