variable === indéfini vs. type de variable === "indéfini"

300

Les directives de style de base de jQuery suggèrent deux façons différentes de vérifier si une variable est définie.

  • Variables globales: typeof variable === "undefined"
  • Variables locales: variable === undefined
  • Propriétés: object.prop === undefined

Pourquoi jQuery utilise-t-il une approche pour les variables globales et une autre pour les variables locales et les propriétés?

Patrick McElhaney
la source
Je ne peux pas répondre à la question de savoir pourquoi JQuery utiliserait les deux approches, mais Javascript a quelques bizarreries intéressantes qui signifient que ces deux choses sont subtilement différentes. Cela ne devrait pas avoir d'importance la plupart du temps (c'est-à-dire si votre code est sain d'esprit), mais il existe néanmoins des différences: voir ici pour un compte rendu
wtfjs.com/2010/02/15/undefined-is-mutable
2
Comme l'a souligné @Struppi, la fonction la plus externe de jQuery a un argument nommé undefined. Dans jQuery, foo === undefinedvérifie la copie locale d'undefined au lieu du global (window.undefined), qui peut avoir été modifiée par un code insensé. Le fait qu'un indéfini soit mutable mérite certainement d'être noté et je suis content que vous l'ayez fait. (+1)
Patrick McElhaney
1
Le lien actuel pour cet article est wtfjs.com/wtfs/2010-02-15-undefined-is-mutable
inscription

Réponses:

366

Pour les variables non déclarées, typeof fooretournera la chaîne littérale "undefined", tandis que le contrôle d'identité foo === undefineddéclencherait l'erreur "foo n'est pas défini" .

Pour les variables locales (dont vous savez qu'elles sont déclarées quelque part), aucune erreur de ce type ne se produirait, d'où le contrôle d'identité.

Linus Kleen
la source
3
@goreSplatter Vous ne pouvez pas le supprimer maintenant. :-) C'était difficile de choisir, mais la façon dont la question est formulée, cette réponse est mieux adaptée. Quiconque s'intéresse à la façon dont les actions non définies en général (comme moi) devraient également consulter les autres réponses, en particulier @ Tim's.
Patrick McElhaney
4
J'ajouterais des guillemets ( typeof foo; // -> "undefined") pour souligner qu'il s'agit d'une chaîne et non de la valeur primitive undefined.
c24w
117

Je m'en tenir à utiliser typeof foo === "undefined"partout. Cela ne peut jamais se tromper.

J'imagine que la raison pour laquelle jQuery recommande les deux méthodes différentes est qu'elles définissent leur propre undefinedvariable dans la fonction dans laquelle réside le code jQuery, donc dans cette fonction undefinedest à l'abri de la falsification de l'extérieur. J'imagine également que quelqu'un quelque part a comparé les deux approches différentes et a découvert que foo === undefinedc'était plus rapide et a donc décidé que c'était la voie à suivre. [MISE À JOUR: comme indiqué dans les commentaires, la comparaison avec undefinedest également légèrement plus courte, ce qui pourrait être une considération.] Cependant, le gain dans les situations pratiques sera tout à fait insignifiant: cette vérification ne sera jamais, jamais une sorte de goulot d'étranglement, et ce que vous perdez est important: l'évaluation d'une propriété d'un objet hôte pour comparaison peut générer une erreur alors qu'untypeof vérifier ne le sera jamais.

Par exemple, ce qui suit est utilisé dans IE pour analyser XML:

var x = new ActiveXObject("Microsoft.XMLDOM");

Pour vérifier s'il dispose d'une loadXMLméthode en toute sécurité:

typeof x.loadXML === "undefined"; // Returns false

D'autre part:

x.loadXML === undefined; // Throws an error

METTRE À JOUR

Un autre avantage du typeofcontrôle que j'ai oublié de mentionner est qu'il fonctionne également avec des variables non déclarées, ce que le foo === undefinedcontrôle ne fait pas, et en fait, il génère un ReferenceError. Merci à @LinusKleen de me l'avoir rappelé. Par exemple:

typeof someUndeclaredVariable; // "undefined"
someUndeclaredVariable === undefined; // throws a ReferenceError

Conclusion: utilisez toujours le typeofchèque.

Tim Down
la source
10
Merci Tim. Votre point sur la performance est logique. L'équipe jQuery est probablement plus préoccupée par l'impact sur la taille du fichier. foo === undefined, lorsqu'il est minimisé, ressemble probablement à quelque chose f===u, alors typeof foo === "undefined"qu'il ne peut être réduit qu'à typeof f==="undefined".
Patrick McElhaney
1
Vous pouvez le définir var u = "undefined"et le réduire à typeof f==u, ce qui améliore les choses mais est encore plus grand.
Tim Down
5
De bons points, mais je ne suis pas sûr que la sécurité typeofcontre les variables non déclarées soit un avantage. Si quoi que ce soit, cela permet aux fautes de frappe de passer plus facilement, et je ne vois pas quand vous voudriez réellement vérifier le type de variables non déclarées.
David Tang
2
@ Box9: Je peux imaginer l'utiliser dans une bibliothèque pour vérifier la présence d'une autre bibliothèque.
Tim Down
2
@jontro: C'est une raison de ne pas utiliser JSLint alors.
Tim Down
29

Encore une autre raison d'utiliser la variante typeof: undefinedpeut être redéfinie.

undefined = "foo";
var variable = "foo";
if (variable === undefined)
  console.log("eh, what?!");

Le résultat de typeof variable ne peut pas.

Mise à jour : notez que ce n'est pas le cas dans ES5 là où le global undefinedest une propriété non configurable, non inscriptible:

15.1.1 Propriétés de la valeur de l'objet global
[...]
15.1.1.3 undefined
La valeur de undefinedest undefined (voir 8.1). Cette propriété a les attributs
{[[Inscriptible]]: false, [[Enumerable]]: false, [[Configurable]]: false}.

Mais il peut toujours être masqué par une variable locale:

(function() {
  var undefined = "foo";
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})()

ou paramètre:

(function(undefined) {
  var variable = "foo";
  if (variable === undefined)
    console.log("eh, what?!");  
})("foo")
Jakob
la source
17
Ne peut pas être redéfini dans ES5.
Ry-
6
La undefinedpropriété globale ne peut pas être redéfinie dans ES5, mais peut toujours être masquée avec une variable locale. void 0est plus court et plus sûr.
Oriol
7

Parce que undefinedn'est pas toujours déclaré, mais jQuery déclare undefineddans sa fonction principale. Ils utilisent donc la undefinedvaleur de sécurité en interne, mais à l'extérieur, ils utilisent le typeofstyle pour être en sécurité.

Struppi
la source
1

Pour les variables locales, la vérification avec localVar === undefinedfonctionnera car elles doivent avoir été définies quelque part dans la portée locale ou elles ne seront pas considérées comme locales.

Pour les variables qui ne sont pas locales et qui ne sont définies nulle part, la vérification lèvera unesomeVar === undefined exception: Uncaught ReferenceError: j n'est pas défini

Voici un code qui clarifiera ce que je dis ci-dessus. Veuillez prêter attention aux commentaires intégrés pour plus de clarté .

function f (x) {
    if (x === undefined) console.log('x is undefined [x === undefined].');
    else console.log('x is not undefined [x === undefined.]');

    if (typeof(x) === 'undefined') console.log('x is undefined [typeof(x) === \'undefined\'].');
    else console.log('x is not undefined [typeof(x) === \'undefined\'].');

    // This will throw exception because what the hell is j? It is nowhere to be found.
    try
    {
        if (j === undefined) console.log('j is undefined [j === undefined].');
        else console.log('j is not undefined [j === undefined].');
    }
    catch(e){console.log('Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.');}

    // However this will not throw exception
    if (typeof j === 'undefined') console.log('j is undefined (typeof(x) === \'undefined\'). We can use this check even though j is nowhere to be found in our source code and it will not throw.');
    else console.log('j is not undefined [typeof(x) === \'undefined\'].');
};

Si nous appelons le code ci-dessus comme ceci:

f();

La sortie serait la suivante:

x is undefined [x === undefined].
x is undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

Si nous appelons le code ci-dessus comme celui-ci (avec n'importe quelle valeur en fait):

f(null); 
f(1);

La sortie sera:

x is not undefined [x === undefined].
x is not undefined [typeof(x) === 'undefined'].
Error!!! Cannot use [j === undefined] because j is nowhere to be found in our source code.
j is undefined (typeof(x) === 'undefined'). We can use this check even though j is nowhere to be found in our source code and it will not throw.

Lorsque vous effectuez la vérification comme ceci:, typeof x === 'undefined'vous demandez essentiellement ceci: veuillez vérifier si la variable xexiste (a été définie) quelque part dans le code source. (plus ou moins). Si vous connaissez C # ou Java, ce type de vérification n'est jamais effectué car s'il n'existe pas, il ne se compilera pas.

<== Fiddle Me ==>

CodageYoshi
la source
1

Résumé:

Quand à portée globale, nous voulons réellement retourner true si la variable n'est pas déclarée ou a la valeur undefined:

var globalVar1;

// This variable is declared, but not defined and thus has the value undefined
console.log(globalVar1 === undefined);

// This variable is not declared and thus will throw a referenceError
console.log(globalVar2 === undefined);

Parce que dans la portée globale, nous ne sommes pas sûrs à 100% si une variable est déclarée, cela pourrait nous donner une référenceError. Lorsque nous utilisons l' typeofopérateur sur la variable inconnue, nous n'obtenons pas ce problème lorsque la variable n'est pas déclarée:

var globalVar1;

console.log(typeof globalVar1 === 'undefined');
console.log(typeof globalVar2 === 'undefined');

Cela est dû au fait que l' typeofopérateur retourne la chaîne undefinedlorsqu'une variable n'est pas déclarée ou détient actuellement la valeur undefinedqui est exactement ce que nous voulons.


  • Avec les variables locales nous n'avons pas ce problème car nous savons au préalable que cette variable existera. Nous pouvons simplement regarder dans la fonction respective si la variable est présente.
  • Avec les propriétés d'objet, nous n'avons pas ce problème car lorsque nous essayons de rechercher une propriété d'objet qui n'existe pas, nous obtenons également la valeur undefined

var obj = {};

console.log(obj.myProp === undefined);

Willem van der Veen
la source
-5

typeof a === 'undefined'est plus rapide que a === 'undefined'd'environ 2 fois sur le nœud v6.9.1.

Eduard Popov
la source
3
Ce ne sont pas les mêmes choses que vous avez tapées. Je pense que vous vouliez dire undefinedà la deuxième partie, pas'undefined'
scaryguy