Wie bekomme ich das globale Objekt in JavaScript?

77

Ich möchte in einem Skript einchecken, ob ein bestimmtes anderes Modul bereits geladen ist.

if (ModuleName) {
    // extend this module
}

Aber wenn ModuleNamees nicht existiert, ist das throws.

Wenn ich wüsste, was das ist, Global Objectkönnte ich das gebrauchen.

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

Aber da ich mein Modul zur Arbeit mit beiden Browsern will und node, rhinoetc., kann ich nicht annehmen window.

Soweit ich weiß, funktioniert dies in ES 5 mit nicht "use strict".

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

Dies schlägt auch mit einer ausgelösten Ausnahme fehl

var MyGLOBAL = window || GLOBAL

Es scheint also, als wäre ich übrig geblieben

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

Keiner dieser Fälle besteht JSLint.

Vermisse ich etwas

coolaj86
quelle
Beachten Sie, dass "var Fn = Function, global = Fn ('return this') ();" wird JSLint nicht übergeben, da JSLint erwartet, dass Funktionen mit einem Großbuchstaben ein Konstruktor sind und mit 'new' aufgerufen werden. Es ist jedoch eine einfache Lösung.
user4815162342
Dies übergibt auch JSLint, und es erfordert keine zusätzliche FnVariable:var global = (function (fn) { return fn('return this'); }(Function));
ahuth
@ahuth Du brauchst ein Extra drin ().
wizzwizz4
Sie können das globale Objekt folgendermaßen abrufen (ohne eval oder Funktionskonstruktor) : var global = (function(){return this}).apply(null). Weitere Infos unter developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Mems
1
Hinweis: apply (null) gibt das globale Objekt nicht an, wenn Sie den strengen Modus verwenden: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mems

Antworten:

95

Nun, Sie können den typeofOperator verwenden, und wenn der Bezeichner an keiner Stelle der Gültigkeitsbereichskette vorhanden ist, wird kein a ausgelöst ReferenceError, sondern nur Folgendes zurückgegeben "undefined":

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

Denken Sie auch daran, dass sich der thisWert für globalen Code auf das globale Objekt bezieht. Wenn sich Ihre ifAnweisung im globalen Kontext befindet, können Sie dies einfach überprüfen this.ModuleName.

In Bezug auf die (function () { return this; }());Technik haben Sie Recht, im strengen Modus wird der thisWert einfach sein undefined.

Im strengen Modus gibt es zwei Möglichkeiten, einen Verweis auf das globale Objekt abzurufen, unabhängig davon, wo Sie sich befinden:

  • Durch den FunctionKonstruktor:

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

Mit dem FunctionKonstruktor erstellte Funktionen erben nicht die Strenge des Aufrufers, sie sind nur dann streng, wenn sie ihren Body mit der 'use strict'Direktive beginnen, andernfalls sind sie nicht streng.

Diese Methode ist mit jeder ES3-Implementierung kompatibel.

  • Durch einen indirekten evalAnruf zum Beispiel:

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

Dies funktioniert, da in ES5 bei indirekten Aufrufen evaldie globale Umgebung sowohl als variable Umgebung als auch als lexikalische Umgebung für den Bewertungscode verwendet wird.

Siehe Details zur Eingabe des Eval-Codes , Schritt 1.

Beachten Sie jedoch, dass die letzte Lösung bei ES3-Implementierungen nicht funktioniert, da bei einem indirekten Aufruf evalvon ES3 die variablen und lexikalischen Umgebungen des Aufrufers als Umgebungen für den Evaluierungscode selbst verwendet werden.

Und schließlich können Sie nützlich sein, um festzustellen, ob der strikte Modus unterstützt wird:

var isStrictSupported = (function () { "use strict"; return !this; })();
Christian C. Salvadó
quelle
5
+1 @CMS - Ich habe nicht gezählt, wie oft ich Ihre Antworten auf dieser Website gelesen habe. Danke, Mann.
screenm0nkey
1
Möglicherweise offensichtliche Frage: Warum "strikt verwenden" und dann eine seiner Auswirkungen umgehen? Gibt es einen Grund, warum der einzige Weg, um den globalen Kontext zu erhalten, ein bisschen hackig ist?
Mowwwalker
1
@Walkerneo Ein Grund könnte sein, dass wir dem globalen Bereich eigentlich nichts hinzufügen sollten (obwohl dies nützlich sein kann, beispielsweise wenn wir eine Bibliotheks-API exportieren müssen). Ich glaube auch, dass die Verwendung von "dies" eingeschränkt ist (im strengen Modus), da es möglicherweise nicht immer offensichtlich ist, worauf sich dies bezieht. Wenn Sie dies erreichen, ist es schwierig, einen Verweis auf das globale Objekt zu erhalten.
Ahuth
4
Leider funktioniert die Funktion ('return this') nicht. In Chrome wird Folgendes angezeigt: EvalError: Die Auswertung einer Zeichenfolge als JavaScript wurde abgelehnt, da 'unsafe-eval' in der folgenden Richtlinie zur Inhaltssicherheitsrichtlinie keine zulässige Skriptquelle ist: "script-src 'self' 'unsafe-inline'".
Pat
1
@CMS Hinweis, der Functionin einem untergeordneten Bereich überschrieben oder sogar beschattet werden kann. Es ist besser zu verwendenvar global = (() => {}).constructor('return this')();
24

Update 2019

Mit all den heutigen Webpacks und Broccolis, Gulps und Grunts, TypeScripts und AltScripts sowie Create-React-Apps usw. ist dies ziemlich nutzlos, aber wenn Sie nur mit einfachem, altem VanillaJS arbeiten und machen möchten es ist isomorph, dies ist wahrscheinlich Ihre beste Option:

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

Der Funktionskonstruktoraufruf funktioniert auch bei Verwendung --use_strictim Knoten, da der Funktionskonstruktor immer in einem globalen, nicht strengen Bereich ausgeführt wird.

Wenn der Funktionskonstruktor fehlschlägt, liegt dies daran, dass Sie sich in einem Browser befinden, dessen evalCSP-Header deaktiviert sind.

Natürlich mit Deno auf dem Weg (der Knoten Ersatz), können sie auch die Funktion Konstruktor nicht zulassen, wobei in diesem Fall ist es an der Rückseite Aufzählen Objekte wie global, module, exports, globalThisund window, und dann die Ente-Typprüfung abschließend die globalen ist ... : - /

Verrückte einzeilige Lösung (Original):

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

.

.

.

Funktioniert

  • in jeder Umgebung (die ich getestet habe)
  • im strengen Modus
  • und sogar in einem verschachtelten Bereich

Update 2014-Sept-23

Dies kann jetzt fehlschlagen, wenn HTTP-Header in den neuesten Browsern die Auswertung ausdrücklich verbieten.

Eine Problemumgehung wäre, die ursprüngliche Lösung zu versuchen / zu fangen, da nur Browser bekannt sind, die diese Art von Teilmenge von JavaScript ausführen.

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
coolaj86
quelle
1
Dies ist nicht nur eine seltsame Eigenart, sondern das, was ich in meiner Antwort als indirekten Aufruf an beschrieben habeeval . In ES5 ist ein Aufruf von nur dann evaldirekt , wenn der CallExpressionvon a gebildet wird MemberExpression, der zwei Bedingungen erfüllt: 1. Der Basiswert der Referenz ist ein Umgebungsdatensatz. 2. Der Referenzname lautet "eval": Jede andere Art des evalAufrufs führt zu einem indirekten Aufruf. Seien Sie vorsichtig, da dieses Verhalten in ES3 nicht funktioniert, da das Konzept der direkten Aufrufe von eval nicht existierte. Versuchen Sie dieses Beispiel mit einer ES3-Implementierung (z. B. IE8)
Christian C. Salvadó
3
@ CoolAJ86, Ihr neuer Code funktioniert auch bei ES3-Implementierungen. Wenn Sie ihn jedoch sorgfältig prüfen, werden Sie feststellen, dass der indirekte evalTeil überhaupt nicht erforderlich ist. Sie benötigen lediglich, var global = Function('return this')();wie ich beschrieben habe, "Funktionen, die mit dem FunctionKonstruktor don erstellt wurden 'erbe nicht die Strenge des Aufrufers ", was bedeutet, dass der Rückgabewert dieser Funktion immer das globale Objekt ist, unabhängig von der Implementierung-. Der evalAufruf auf der rechten Seite des ||Operators wird niemals erfolgen, da die Funktion immer einen wahrheitsgemäßen Wert liefert (das globale Objekt).
Christian C. Salvadó
1
Sie können die Komma-Verwirrung erhöhen, indem Sie angeben a = 0, 1; // 0und (a = 0), 1; // 0beide Ausdrücke zurückkehren 1. Vielleicht // a = 0wäre es besser.
MikeM
Hmm ... ich hätte schwören können, als ich es getestet habe, ich habe 0 ... aber du hast definitiv recht. aktualisiert die Antwort
coolaj86
Wann wird der Ausdruck (42, eval)('this')ausgewertet? Mit anderen Worten, wann wird Function('return this')()es falsch sein?
zVictor
6

Warum verwenden Sie dies nicht einfach wie folgt in einem globalen Bereich als Parameter für eine Wrapper-Funktion?

(function (global) {
    'use strict';
    // Code
}(this));
Edygar de Lima Oliveira
quelle
2
Überraschend, dass noch niemand darauf hingewiesen hat, dass dies nicht wirklich etwas hinzufügt, was Szabolcs Kurdis Antwort noch nicht ein Jahr zuvor geliefert hat. Es geht nicht auf das Problem ein, das in den dortigen Kommentaren angesprochen wurde, nämlich, dass es im globalen Bereich aufgerufen werden muss , um zu funktionieren, aber zumindest Ihre Antwort bestätigt dies.
Dies funktioniert in einem NodeJS-Modul nicht this === module.exports.
Lapo
4

Bitte schön :)

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

Dies sollte von überall aus funktionieren, beispielsweise von einem anderen Verschluss aus.

Bearbeiten - lesen Sie einfach Ihren Beitrag genauer durch und sehen Sie den Teil über den strengen ES5-Modus. Kann jemand mehr Licht ins Dunkel bringen? Dies war der akzeptierte Weg, um das globale Objekt zu erhalten, solange ich mich erinnern kann ... Ich hoffe, dass es nicht kaputt geht.

Bearbeiten 2 - Die Antwort von CMS enthält weitere Informationen zur Behandlung von ES5 im strengen Modus this.

Dagg Nabbit
quelle
@Eduardo ―Keine.
8онстантин Ван
3

Ich denke, das ist in Nashorn, Knoten, Browser und mit jslint (ohne zusätzliche Workaround-Flags) ziemlich in Ordnung - würde dies helfen? Vermisse ich etwas

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

Obwohl ich selbst dazu neige, das Fensterobjekt zu verwenden, und wenn ich kopflose Tests benötige, kann ich env.js (Nashorn) oder Phantom (Knoten) verwenden.


quelle
Es besteht jslint ohne zusätzliche Optionen (wenn Sie das Beispiel x = 1 entfernen), obwohl es meiner Meinung nach nur eine Alternative ist (während Eleganz ein sehr subjektiver Faktor ist).
2
Sie vermissen die Tatsache, dass thismöglicherweise nicht auf das globale Objekt verwiesen wird.
MikeM
Soweit ich weiß, bezieht es sich auf den globalen Bereich, wenn es im globalen Bereich (Browser, Nashorn, Knoten) verwendet wird, aber ich kann mich irren. Können Sie ein Beispiel vm zeigen, wo es anders funktioniert? Vielen Dank!
1
@SzabolcsKurdi Sie haben Recht, dass 'dies' der globale Bereich ist, wenn es im globalen Bereich verwendet wird. Das Problem ist, dass es keine Garantie dafür gibt, dass die Funktion im globalen Bereich ausgeführt wird. Zum Beispiel, wenn dies in eine Bibliothek gestellt und in eine sofort aufgerufene Funktion eingeschlossen wird. Was wir wirklich suchen, ist eine Möglichkeit, den globalen Geltungsbereich unabhängig vom Geltungsbereich zu erhalten, in dem er aufgerufen wird.
ahuth
2

ECMAScript wird dies in Kürze zu seinem Standard hinzufügen: https://github.com/tc39/proposal-global

Bis es fertig ist, wird Folgendes empfohlen:

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');
};
Shaun Lebron
quelle
Das ist problematisch; Alle Versionen vor 2.9 sind betroffen und Moment ist eine sehr beliebte Bibliothek.
Knu
1

Dies geht nicht an jslint vorbei: var Fn = Function, global = Fn('return this')();

Probieren Sie es selbst aus: http://www.jslint.com/

dieser Wille: var Fn = Function, global = new Fn('return this')();

Aber effektiv sind das laut MDN dasselbe :

Das Aufrufen des Funktionskonstruktors als Funktion (ohne Verwendung des neuen Operators) hat den gleichen Effekt wie das Aufrufen als Konstruktor.

senz
quelle
Interessante Beobachtung.
L0j1k
1

Diese folgende Lösung funktioniert in:

  • Chrom
  • Node.JS
  • Feuerfuchs
  • MSIE
  • Web Worker

Der Code lautet:

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

Sie müssen nur X für den Namen der Variablen ändern, die Sie möchten

Remo H. Jansen
quelle
0

Ich hatte dieses Problem schon einmal, ich bin mit der Lösung nicht zufrieden, aber es funktioniert und übergibt JSLint (Browser annehmen | Knoten annehmen):

"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");
}

Sobald Sie die GLOBAL-Variable haben, können Sie Ihre Überprüfung durchführen und am Ende des Skripttyps

delete GLOBAL.GLOBAL;
Roderick Obrist
quelle
Ich bestätige, dass dieser Ansatz sauber und nützlich ist. Ich bin mir nicht sicher, warum es im Vergleich zu all den bösen Lösungen in anderen Antworten nicht mehr positiv bewertet wird.
0

Folgendes verwende ich:

"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';
        }
    }
}
Lorenz Lo Sauer
quelle