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 throw
s.
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?
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; })();
var global = Function('return this')() || (42, eval)('this');
.
.
.
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;
}
`` `
(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');
}());
En bref: c'est une bizarrerie bizarre. Voir les commentaires ci-dessous (ou le post ci-dessus)
Dans strict mode
this
n'est jamais le global, mais aussi dans strict mode
eval
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
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;
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).
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.
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');
};
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.
Cette solution suivante fonctionne dans:
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
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';
}
}
}