web-dev-qa-db-fra.com

Comment obtenir l'objet global en JavaScript?

Je veux vérifier dans un script si un certain autre module est déjà chargé.

if (ModuleName) {
    // extend this module
}

Mais si ModuleName n'existe pas, alors throws.

Si je connaissais le Global Object, je pourrais l'utiliser. 

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

Mais comme je veux que mon module fonctionne avec les deux navigateurs et node, rhino, etc., je ne peux pas supposer window.

Si je comprends bien, cela ne fonctionne pas dans ES 5 avec "use strict";

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

Cela échouera également avec une exception levée

var MyGLOBAL = window || GLOBAL

Il semble donc que je reste avec 

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

Aucun de ces cas ne passera JSLint.

Est-ce que je manque quelque chose?

71
CoolAJ86

Eh bien, vous pouvez utiliser l'opérateur typeof, et si l'identifiant n'existe nulle part dans la chaîne de la portée, il not lance une ReferenceError, il retournera simplement "undefined":

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

Rappelez-vous également que la valeur this du code global fait référence à l'objet global, ce qui signifie que si votre instruction if se trouve dans le contexte global, vous pouvez simplement cocher this.ModuleName.

A propos de la technique (function () { return this; }());, vous avez raison, en mode strict, la valeur this sera simplement undefined.

En mode strict, il existe deux manières d'obtenir une référence à l'objet Global, où que vous soyez:

  • Via le constructeur Function:

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

Les fonctions créées avec le constructeur Function n'héritent pas de la rigueur de l'appelant, elles ne le sont que si leur corps commence par la directive 'use strict', sinon elles ne le sont pas.

Cette méthode est compatible avec toute implémentation ES3.

  • Par un appel indirect eval, par exemple:

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

Ce qui précède fonctionnera, car dans ES5, les appels indirects à eval utilisent le environnement global comme environnements de variable et lexical pour le code eval.

Voir les détails sur Entrée du code d'évaluation , étape 1.

Sachez toutefois que la dernière solution ne fonctionnera pas avec les implémentations ES3, car un appel indirect à eval sur ES3 utilisera les environnements lexical et variable de l'appelant comme environnements du code eval lui-même.

Et enfin, vous pouvez trouver utile de détecter si le mode strict est pris en charge:

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

Solution folle à une ligne:

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

.

.

.

Travaux

  • dans chaque environnement (que j'ai testé)
  • en mode strict
  • et même dans une portée imbriquée

Mise à jour 2014-sept-23

Cela peut maintenant échouer si les en-têtes HTTP des derniers navigateurs interdisent explicitement eval.

Une solution de contournement consisterait à essayer/intercepter la solution d'origine car seuls les navigateurs sont connus pour exécuter ce type de sous-ensemble de JavaScript.

var global;

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

`` `

Exemple:

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

}());

Testé:

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

Pourquoi:

En bref: c'est une bizarrerie bizarre. Voir les commentaires ci-dessous (ou le post ci-dessus)

Dans strict modethis n'est jamais le global, mais aussi dans strict modeeval opère dans un contexte séparé dans lequel this est toujours le global.

En mode non strict, this est le contexte actuel. S'il n'y a pas de contexte actuel, cela suppose le global. Une fonction anonyme n'a pas de contexte et donc en mode non strict, assume la fonction globale.

Sous Rant:

Il y a une erreur stupide de JavaScript que 99,9% du temps confond simplement des personnes appelées «opérateurs de virgule».

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
18
CoolAJ86

Pourquoi ne pas simplement utiliser ceci dans une portée globale en tant que paramètre d'une fonction wrapper, comme suit?

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

J'avais ce problème auparavant, je ne suis pas satisfait de la solution, mais cela fonctionne et passe JSLint (assume le navigateur | suppose le noeud):

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

une fois que vous avez la variable GLOBAL, vous pouvez effectuer votre vérification et à la fin du type de script

delete GLOBAL.GLOBAL;
2
Roderick Obrist

Je pense que c'est assez bien dans rhino, noeud, navigateur et avec jslint (sans drapeaux de contournement supplémentaires) - cela aiderait-il? Est-ce que je manque quelque chose?

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

Bien que j'ai moi-même tendance à utiliser l'objet window et si j'ai besoin de tests sans tête, je peux utiliser env.js (rhino) ou Phantom (noeud).

2
user1410117

Voici :)

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

Cela devrait fonctionner n'importe où, par exemple depuis une autre fermeture.

Modifier - il suffit de lire votre message plus attentivement et de lire la partie concernant le mode strict ES5. Quelqu'un peut-il apporter plus de lumière à ce sujet? Cela a été le moyen accepté d’obtenir l’objet global aussi longtemps que je me souvienne ... j’espère bien qu’il ne finira pas par se briser.

Edit 2 - La réponse de CMS contient plus d'informations sur le traitement de this en mode strict ES5.

2
Dagg Nabbit

ECMAScript va bientôt ajouter cela à sa norme: https://github.com/tc39/proposal-global

En attendant, voici ce qui est recommandé:

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');
};
1
Shaun Lebron

Ceci ne passe pas jslint: var Fn = Function, global = Fn('return this')();

Essayez vous-même: http://www.jslint.com/

ce sera: var Fn = Function, global = new Fn('return this')();

Mais effectivement, ce sont la même chose selon MDN

Invoquer le constructeur de fonction en tant que fonction (sans utiliser l'opérateur new) a le même effet que l'invoquer en tant que constructeur.

1
senz

Cette solution suivante fonctionne dans:

  • Chrome
  • Node.JS
  • Firefox
  • MSIE
  • Travailleurs Web

Le code est:

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

Il vous suffit de changer X pour le nom de la variable que vous souhaitez 

0
Remo H. Jansen

Voici ce que j'utilise:

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