À quel point est-il vraiment dangereux en JavaScript de supposer qu'undefined n'est pas écrasé?

86

Chaque fois que quelqu'un mentionne un test undefined, il est signalé que ce undefinedn'est pas un mot-clé, il peut donc être défini sur"hello" , vous devriez donc l'utiliser à la typeof x == "undefined" place. Cela me semble ridicule. Personne ne ferait jamais ça, et s'ils le faisaient, ce serait une raison suffisante pour ne jamais utiliser le code qu'ils ont écrit ... n'est-ce pas?

J'ai trouvé un exemple de quelqu'un qui s'est accidentellement mis undefinedà null, et cela a été donné comme une raison pour éviter de supposer que ce undefinedn'est pas écrasé. Mais s'ils avaient fait cela, le bogue n'aurait pas été détecté, et je ne vois pas en quoi c'est mieux.

En C ++, tout le monde sait que c'est légal de le dire #define true false, mais personne ne vous conseille jamais d'éviter trueet d'utiliser à la 0 == 0place. Vous supposez simplement que personne ne serait jamais assez gros pour faire ça, et s'ils le font, ne faites plus jamais confiance à leur code.

Cela a-t-il déjà mordu quelqu'un où quelqu'un d'autre a été assigné undefined(exprès) et cela a cassé votre code, ou est-ce plus une menace hypothétique? Je suis prêt à tenter ma chance pour rendre mon code légèrement plus lisible. Est-ce vraiment une mauvaise idée?

Pour répéter, je ne demande pas comment se protéger contre les réaffectations indéfinies. J'ai déjà vu ces astuces écrites 100 fois. Je demande à quel point il est dangereux de ne pas utiliser ces astuces.

Cosmologicon
la source
7
Je serais heureux de le faire, mais je ne pense pas qu'aucune des trois réponses données ne répond bien à la question. J'ai essayé de faire comprendre que je ne demandais pas une répétition des réponses auxquelles je me suis lié, mais c'est toujours ce que j'ai obtenu. Si c'est considéré comme gauche de ne pas accepter une réponse même si ce n'est pas ce que je demandais, faites-le moi savoir et j'accepterai une!
Cosmologicon
1
Je pense que tous les faits sont exposés ici, cependant. Donc, ce que vous dites, c'est que vous voulez plutôt une réponse ou une opinion subjective, ce qui ne mène qu'à un désaccord. C'est la raison pour laquelle l'expression «meilleures pratiques» n'est pas autorisée dans les titres des questions. Vous êtes la seule personne à savoir à quel point c'est dangereux dans votre scénario. Et si vous écrivez une bibliothèque populaire où vous ne contrôlez pas toutes les 'variables', l'encapsuleur de fonction (non défini) {} semble être une excellente réponse. Pourquoi ne pas l'utiliser et accepter cette réponse?
shannon
4
Si quelqu'un craint que quelqu'un redéfinisse undefined, vous devriez vous inquiéter davantage de quelqu'un qui redéfinit XMLHttpRequest, ou alert. Toutes les fonctions que nous utilisons windowauraient pu être redéfinies. Et si vous vous inquiétez simplement qu'un collègue le fasse par accident, pourquoi lui faire confiance pour ne pas le faire window.addEventListener = "coolbeans"? La réponse est de ne pas s'inquiéter de tout cela. Si quelqu'un injecte malicieusement JS dans votre page, vous êtes quand même arrosé. Essayez d'éviter que cela ne se produise en premier lieu.
Chris Middleton

Réponses:

56

Non, je ne l'ai jamais fait. C'est principalement parce que je développe sur des navigateurs modernes, qui sont pour la plupart conformes à ECMAScript 5. La norme ES5 dicte qu'il undefinedest désormais en lecture seule. Si vous utilisez le mode strict (vous devriez), une erreur sera générée si vous essayez accidentellement de le modifier.

undefined = 5;
alert(undefined); // still undefined
'use strict';
undefined = 5; // throws TypeError

Ce que vous ne devez pas faire est de créer votre propre portée, modifiable undefined:

(function (undefined) {
    // don't do this, because now `undefined` can be changed
    undefined = 5;
})();

Constant va bien. Encore inutile, mais bien.

(function () {
    const undefined = void 0;
})();
Ry-
la source
Votre première déclaration garantit seulement que vous n'écrasez pas accidentellement un fichier indéfini pendant le développement, cela pose toujours la même menace lors de l'exécution avec un autre code sur des ordinateurs clients.
bennedich
1
@bennedich: Je sais, je disais que je n'ai jamais rencontré ce problème, mais voici ce que vous devriez faire .
Ry-
1
@bennedich N'avez-vous pas la même chance d'écraser accidentellement une autre variable?
Ates Goral
40

Aucun code approprié ne fera une telle chose. Mais vous ne pouvez jamais savoir ce que certains développeurs intelligents en herbe ou un plugin / bibliothèque / script que vous utilisez ont fait. D'un autre côté, il est extrêmement improbable et les navigateurs modernes ne permettront pas du tout l'écrasement undefined, donc si vous utilisez un tel navigateur pour le développement, vous remarquerez rapidement si un code tente de l'écraser.


Et même si vous ne l'avez pas demandé, de nombreuses personnes trouveront probablement cette question en recherchant le problème le plus courant de «comment se protéger contre une redéfinition undefined», je vais donc y répondre quand même:

Il existe un très bon moyen d'obtenir un vraiment indéfini, undefined quel que soit l'âge du navigateur:

(function(undefined) {
    // your code where undefined is undefined
})();

Cela fonctionne car un argument qui n'est pas spécifié est toujours undefined . Vous pouvez également le faire avec une fonction qui accepte de vrais arguments, par exemple comme celui-ci lorsque vous utilisez jQuery. C'est généralement une bonne idée de garantir un environnement sain de cette manière:

(function($, window, undefined) {
    // your code where undefined is undefined
})(jQuery, this);

Ensuite, vous pouvez être sûr qu'à l'intérieur de cette fonction anonyme, les choses suivantes sont vraies:

  • $ === jQuery
  • window === [the global object]
  • undefined === [undefined].

Cependant, notez que parfois typeof x === 'undefined'est réellement nécessaire: si la variable xn'a jamais été définie sur une valeur (contrairement à celle définie sur undefined), la lecture xd'une manière différente, par exemple if(x === undefined), provoquera une erreur. Cela ne s'applique pas aux propriétés des objets, donc si vous savez que yc'est toujours un objet, if(y.x === undefined)c'est parfaitement sûr.

ThiefMaster
la source
2
Votre déclaration concernant le fait de xne pas être défini sur une valeur n'est pas tout à fait vraie (voir jsfiddle.net/MgADz ); c'est plutôt s'il n'est vraiment pas défini (voir jsfiddle.net/MgADz/1 ).
Ry-
7
Encore une fois, si quelqu'un dit accidentellement que undefined = someVariablec'est un bug et que vous voulez que les choses se cassent. Au moins je le fais.
Cosmologicon
2
J'ai travaillé avec une base de code qui avait un script hérité minifié qui écrasait undefined, nous n'avions ni le temps ni le budget pour réécrire le script, c'est un exemple où typeof x == "undefined" était requis, ce n'est pas si étrange et probablement une bonne habitude.
Damen TheSifter
2
Je suis intéressé par pourquoi tous les exemples utilisent "undefined" comme argument supplémentaire dans la fonction? Mais il peut y avoir une situation si quelqu'un passe un paramètre supplémentaire (défini) par erreur et toute logique échouera. Pourquoi ne pas utiliser quelque chose comme function () {var _undef; if (someVal == _undef) {faire quelque chose}};
Oleksandr_DJ
4
@Oleksandr_DJ Exactement. Faire une valeur que vous connaissez est undefinedet assignons à undefinedportée. Ne laissez pas undefinedouvert pour l'écrasement si vous passez "trop" d'arguments. Cela invite à des erreurs et constitue un modèle intrinsèquement non défensif, en particulier lorsque, comme le souligne Lucero , il existe des alternatives très simples pour obtenir des undefinedvaleurs solides dans la portée.
ruffin
19

Il y a une solution simple à cela: comparer void 0ce qui n'est toujours pas défini.

Notez que vous devez éviter ==car cela peut contraindre les valeurs. Utilisez plutôt ===(et !==).

Cela dit, la variable non définie peut être définie par erreur si quelqu'un écrit =au lieu de ==comparer quelque chose avec undefined.

Lucero
la source
OMI, c'est la meilleure réponse.
abhisekp
1
Considérez que cela foo === void 0peut ne pas se lire aussi facilement foo === undefinedet qu'immuable undefinedest entièrement pris en charge par les navigateurs modernes (IE 9+) comme vous pouvez le voir dans ce tableau de compatibilité.
Daniel AR Werner
3

Vous seul savez quel code vous utilisez, et donc à quel point il est dangereux. On ne peut pas répondre à cette question de la manière dont vous avez précisé que vous voulez qu'elle réponde.

1) Créez une stratégie d'équipe, interdisez de redéfinir undefined, en la réservant pour son utilisation plus populaire. Scannez votre code existant pour une affectation à gauche non définie.

2) Si vous ne contrôlez pas tous les scénarios, si votre code est utilisé en dehors de situations que vous ou vos politiques contrôlez, alors votre réponse est évidemment différente. Scannez le code qui utilise vos scripts. Heck, scannez le Web pour des statistiques sur l'affectation de gauche non définie si vous le souhaitez, mais je doute que cela ait été fait pour vous, car il est plus facile de simplement poursuivre la réponse n ° 1 ou n ° 3 ici à la place.

3) Et si cette réponse n'est pas assez bonne, c'est probablement parce que, encore une fois, vous avez besoin d'une réponse différente. Peut-être que vous écrivez une bibliothèque populaire qui sera utilisée dans les pare-feu d'entreprise et que vous n'avez pas accès au code d'appel. Ensuite, utilisez l'une des autres bonnes réponses ici. Notez que la bibliothèque jQuery populaire pratique l'encapsulation sonore et commence:

(function( window, undefined ) {

Vous seul pouvez répondre à votre question de la manière spécifique que vous recherchez. Que dire de plus?

edit: ps si vous voulez vraiment mon avis, je vais vous dire que ce n'est pas du tout dangereux. Tout ce qui serait si susceptible de causer des défauts (comme l'attribution à undefined, qui est évidemment un comportement à risque bien documenté) est en soi un défaut. C'est le défaut qui est le risque. Mais ce n'est que dans mes scénarios, où je peux me permettre d'avoir cette perspective. Comme je vous le recommande, j'ai répondu à la question pour mes cas d'utilisation.

Shannon
la source
3

Il est sûr de tester contre undefined. Comme vous l'avez déjà mentionné. Si vous arrivez à un code qui le remplace (ce qui est hautement améliorable), ne l'utilisez plus.

Peut - être que si vous créez une bibliothèque à usage public, vous pouvez utiliser certaines des techniques pour éviter que l'utilisateur ne la modifie. Mais même dans ce cas, c'est leur problème, pas votre bibliothèque.

Bart Calixto
la source
2

Vous pouvez utiliser undefineddans votre code lors du codage pour les navigateurs prenant en charge ECMAScript 5.1 car il est immuable selon la spécification du langage .

Voir également ce tableau de compatibilité ou ce caniuse ECMAScript 5 pour voir que tous les navigateurs modernes (IE 9+) ont implémenté immuableundefined .

Daniel AR Werner
la source
1

Ce n'est pas du tout dangereux. Il ne peut être écrasé que lors de l'exécution sur un moteur ES3 et il est peu probable que ce soit plus utilisé.

kirk.burleson
la source
0

Tout d'abord, si votre code casse, ce n'est probablement pas parce qu'un autre développeur "essaie d'être un imbécile" comme vous le dites.

C'est vrai que ce undefinedn'est pas un mot-clé. Mais il est un niveau mondial primitif. Il était destiné à être utilisé comme ceci (voir "non défini" sur developer.mozilla.org ):

var x;
if (x === undefined) {
    // these statements execute
}
else {
    // these statements do not execute
}

L'alternative commune à cela (également de MDN ) et à mon avis la meilleure façon est:

// x has not been declared before
if (typeof x === 'undefined') { // evaluates to true without errors
    // these statements execute
}

if(x === undefined){ // throws a ReferenceError

}

Ce qui présente quelques avantages, le plus évident (d'après les commentaires) est qu'il ne déclenche pas d'exception lorsque x n'est pas déclaré. Il convient également de noter que MDN souligne également qu'il est important d'utiliser ===over ==dans le premier cas car:

var x=null;
if (x === undefined) {
    // this is probably what you meant to do
    // these lines will not execute in this case
}
else if (x == undefined) {
    // these statements will execute even though x *is* defined (as null)
}
else {
    // these statements do not execute
}

C'est une autre raison souvent négligée pour laquelle il est probablement préférable d'utiliser simplement la deuxième alternative dans tous les cas.

Conclusion: il n'est pas faux de le coder de la première manière, et certainement pas dangereux. L'argument que vous avez vu que vous utilisez comme exemple contre lui (qu'il peut être écrasé) n'est pas l'argument le plus fort pour coder l'alternative avec typeof. Mais l'utilisation typeofest plus forte pour une raison en particulier: elle ne lève pas d'exception lorsque votre var n'est pas déclarée. On pourrait également faire valoir que l'utilisation ==au lieu de ===est une erreur courante, auquel cas elle ne fait pas ce que vous attendiez. Alors pourquoi ne pas l'utiliser typeof?

Poulpe
la source
1
"Ce qui présente quelques avantages, le plus évident est qu'il ne déclenche pas d'exception lorsque x n'est pas déclaré." Dit moi si j'ai bien compris. Vous pensez que c'est un avantage d'ignorer silencieusement l'exception qui se produit lorsque vous utilisez une variable non déclarée? Cela semble très sujet aux erreurs. Pouvez-vous expliquer pourquoi vous pensez que ce n'est pas un terrible inconvénient?
Cosmologicon