如何在JavaScript中获取全局对象?


77

我想检查脚本是否已加载其他模块。

if (ModuleName) {
    // extend this module
}

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

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

if (window.ModuleName) {
    // extend this 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。

我有什么想念的吗?


请注意,“ var Fn = Function,global = Fn('return this')();” 会不会通过JSLint的,因为JSLint的预计用大写字母的功能是一个构造函数,并与“新”的调用。不过,这很简单。
user4815162342 '10 -10-10

这也通过了JSLint,并且不需要额外的Fn变量:var global = (function (fn) { return fn('return this'); }(Function));
2013年

@ahuth您()在那里需要一个额外的东西。
wizzwizz4 2016年

你可以得到这样的全局对象(不EVAL或功能构造)var global = (function(){return this}).apply(null)。更多的相关信息在developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
MEMS

1
注:适用(空)不给,如果你使用的是严格的模式的全局对象:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
MEMS

Answers:


95

好吧,您可以使用typeof运算符,并且如果标识符在范围链的任何位置都不存在,则不会抛出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全局环境用作评估代码的变量环境和词法环境。

查看有关输入评估码(步骤1)的详细信息。

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

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

var isStrictSupported = (function () { "use strict"; return !this; })();

5
+1 @CMS-我已经失去了在该网站上阅读您的答案的次数。谢啦。
screenm0nkey 2011年

1
可能显而易见的问题:为什么要使用“严格使用”,然后规避其影响之一?是否有理由获取全局上下文的唯一方法有点棘手?
mowwwalker

1
@Walkerneo的一个原因可能是我们实际上不应该在全局范围内添加任何内容(尽管它很有用,例如当我们需要导出库API时)。另外,我相信“ this”的使用受到限制(在严格模式下),因为它所指的可能并不总是很明显。要做到这一点,将很难获得对全局对象的引用。
2013年

4
可悲的是,Function('return this')不起作用。在Chrome中,我得到:EvalError:拒绝将字符串评估为JavaScript,因为以下内容安全策略指令中不允许使用'unsafe-eval'脚本源:“ script-src'self''unsafe-inline'”。
专利

1
@CMS请注意,它Function可以在子作用域中被覆盖甚至阴影化。最好使用var global = (() => {}).constructor('return this')();

24

更新2019

对于当今的所有Webpack和Broccolis,Gulps和Grunts,TypeScript和AltScripts,以及create-react-apps等,这都是毫无用处的,但是如果您只使用普通的,旧的VanillaJS,并且想要制作同构,这可能是您最好的选择:

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

即使在--use_strictin节点中使用时,Function构造函数调用也将起作用,因为Function构造函数始终在全局非严格范围内执行。

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

当然,与杰诺的方式(节点替换),它们也不允许该功能的构造,在这种情况下,它回到枚举对象,如globalmoduleexportsglobalThiswindow,然后鸭式检查这是全球详尽... :-/

疯狂的一线解决方案(原始):

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

1
这不只是一个奇怪的怪癖,而是我在回答中描述为的间接调用eval。在ES5中,当调用由满足以下两个条件的a构成时才eval直接调用:1.引用的基值是环境记录。2.引用名称为,任何其他调用方式都将导致间接调用。请注意,因为这种行为在ES3中不起作用,因为不存在直接调用eval的概念。尝试使用ES3实现(例如IE8)的示例CallExpressionMemberExpression"eval"eval
Christian C.Salvadó11年

3
@ CoolAJ86,您的新代码也可以在ES3实现上使用,但是如果仔细检查,您会发现eval根本不需要间接部分,您所需要的只是var global = Function('return this')();,正如我所描述的,“使用Function构造函数创建的函数don “不继承调用者的严格性”,这意味着该函数的返回值将始终是全局对象(无论实现如何)。该eval在右边的呼叫||运营商,绝不会进行,因为该功能总会产生truthy值(全球OBJ)。
Christian C.Salvadó2011年

1
您可能会通过声明a = 0, 1; // 0(a = 0), 1; // 0两个表达式都返回来增加逗号的混乱1。也许// a = 0会更好。
MikeM 2013年

嗯...测试时我可能发誓我的分数是0 ...但是您绝对正确。更新了答案
coolaj86

表达式何时 (42, eval)('this')被评估?换句话说,什么时候会Function('return this')()是假的?
zVictor

6

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

(function (global) {
    'use strict';
    // Code
}(this));

2
令人惊讶的是没有人指出,这并没有增加Szabolcs Kurdi一年前尚未提供的答案。它没有解决那里的评论中提出的问题,即它需要在全局范围内调用才能起作用,但是至少您的回答确实承认这一点。

这不适用于NodeJS模块中的this === module.exports
lapo

4

干得好 :)

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

这应该在任何地方都可以工作,例如在另一个闭包内部。

编辑-只是更仔细地阅读您的文章,并了解有关ES5严格模式的部分。谁能进一步阐明这一点?只要我还记得,这就是获取全局对象的一种公认方法。我希望它最终不会被破坏。

编辑2-CMS的答案具有有关ES5严格模式对的处理的更多信息this


@Eduardo ―没有。
КонстантинВан”,19年

3

我认为在犀牛,节点,浏览器以及jslint(没有其他解决方法标志)中这几乎可以-这有帮助吗?我想念什么吗?

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

虽然我本人倾向于使用window对象,但如果我需要无头测试,则可以使用env.js(犀牛)或Phantom(节点)。


它传递了jslint,没有其​​他选项(如果去除x = 1的示例),尽管这只是我的选择(尽管优雅是一个非常主观的因素)。

2
您遗漏了this可能未引用全局对象的事实。
MikeM 2013年

据我所知,如果在全局范围(浏览器,犀牛,节点)中使用的话,它确实指向全局范围,但是我可能是错的。您能否显示示例vm的不同工作方式?谢谢!

1
@SzabolcsKurdi是正确的,当在全局范围中使用时,“ this”将成为全局范围。问题在于不能保证该函数将在全局范围内执行。例如,如果将此获取放入库中并包装在立即调用的函数中。我们真正寻求的是一种方式来获得全球范围内,无论这就是所谓的范围。
ahuth

2

ECMAScript将很快将其添加到其标准中:https : //github.com/tc39/proposal-global

在完成之前,建议这样做:

var getGlobal = function () {
    // the only reliable means to get the global object is
    // `Function('return this')()`
    // However, this causes CSP violations in Chrome apps.
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
};

是有问题的;2.9之前的所有版本均受到影响,Moment是一个非常受欢迎的库。
Knu

1

这没有传递jslint: var Fn = Function, global = Fn('return this')();

自己尝试:http : //www.jslint.com/

这将: var Fn = Function, global = new Fn('return this')();

但实际上,根据MDN来说,这些是同一回事:

将Function构造函数作为函数调用(不使用new运算符)与作为构造函数调用它具有相同的效果。


有趣的观察。
L0j1k 2014年

1

以下解决方案适用于:

  • 节点JS
  • 火狐浏览器
  • 微软IE
  • 网络工作者

代码是:

(function (__global) {
  // __global here points to the global object
})(typeof window !== "undefined" ? window : 
   typeof WorkerGlobalScope !== "undefined" ? self :
   typeof global !== "undefined" ? global :
   Function("return this;")());

您只需要将X更改为您想要的变量名称即可


0

我之前遇到过这个问题,我对解决方案不满意,但是它可以正常工作并传递JSLint(假设browser | assume节点):

"use strict";
var GLOBAL;
try{
    /*BROWSER*/
    GLOBAL = window;
}catch(e){
    /*NODE*/
    GLOBAL = global;
}
if(GLOBAL.GLOBAL !== GLOBAL){
    throw new Error("library cannot find the global object");
}

一旦拥有GLOBAL变量,就可以进行检查,并且在脚本类型的末尾

delete GLOBAL.GLOBAL;

我确认这种方法是干净且有用的。不知道为什么与其他答案中的所有邪恶解决方案相比,它没有得到更多支持。

0

这是我正在使用的:

"use strict";
if(this && this.hasOwnProperty && !this.hasOwnProperty('globalScope')){
    try {
        globalScope = Function('return this')();
    }catch(ex){
        if(this.hasOwnProperty('window')){
            globalScope = window;
        }else{
            throw 'globalScope not found';
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.