Quelle est la différence entre (NaN! = NaN) et (NaN! == NaN)?

148

Tout d'abord, je tiens à mentionner que je sais comment isNaN()et Number.isNaN()travaille. Je lis le guide défini de David Flanagan et il donne un exemple pour savoir comment vérifier si la valeur est NaN:

x !== x

Cela se traduira par truesi et seulement si xest NaN.

Mais maintenant, j'ai une question: pourquoi utilise-t-il une comparaison stricte? Parce qu'il semble que

x != x

se comporte de la même manière. Est-il sûr d'utiliser les deux versions, ou il me manque une ou plusieurs valeurs dans JavaScript qui retourneront truepour x !== xet falsepour x != x?

Giorgi Nakeuri
la source
10
Il se peut que Flanagan préfère simplement les !==chèques aux !=chèques. Autant que je sache, il n'y a pas d'autre valeur où x != x. Mais il existe deux groupes distincts de développeurs JavaScript: ceux qui préfèrent !=et ceux qui préfèrent !==, que ce soit pour la vitesse, la clarté, l'expressivité, etc.
Steve Klösters
30
Pourquoi utiliser une comparaison lâche lorsque la comparaison stricte se comporte de la même manière?
Ry-
3
@Raulucco: NaNn'est pas un type unique, c'est un nombre. C'est une valeur unique qui n'est pas égale à elle-même.
TJ Crowder
8
Le titre semble induire les gens en erreur. Je suggérerais de le changer en quelque chose comme "x! = X est-il toujours différent de x! == x?"
TJ Crowder
6
@femmestem: Giorgi a dit que "dans ce cas" c'est une question de style. Et il a raison là-dessus. Il n'est pas le style quand les types des opérandes sont différents, mais il est de style quand ils sont les mêmes. Séparément: Flanagan fait ces comparaisons ===avec NaN pour faire valoir que NaN n'est pas égal à lui-même. Il n'a pas «tort», il le fait comme un exercice d'enseignement, démontrant que cela ne fonctionne pas.
TJ Crowder

Réponses:

128

Tout d'abord, permettez-moi de souligner que NaNc'est une valeur très spéciale: par définition, elle n'est pas égale à elle-même. Cela vient de la norme IEEE-754 sur laquelle s'appuient les numéros JavaScript. La valeur "pas un nombre" n'est jamais égale à elle-même, même lorsque les bits correspondent exactement. (Ce qu'ils ne sont pas nécessairement dans IEEE-754, cela permet plusieurs valeurs différentes "pas un nombre".) C'est pourquoi cela arrive même; toutes les autres valeurs en JavaScript sont égales à elles-mêmes, NaNc'est juste spécial.

... est-ce que je manque une valeur dans JavaScript qui retournera true pour x! == x et false pour x! = x?

Non, tu ne l'es pas. La seule différence entre !==et !=est que ce dernier effectuera une coercition de type si nécessaire pour que les types des opérandes soient identiques. Dans x != x, les types d'opérandes sont les mêmes, et donc exactement les mêmes que x !== x.

Ceci est clair dès le début de la définition de l' opération abstraite d'égalité :

  1. ReturnIfAbrupt (x).
  2. ReturnIfAbrupt (y).
  3. Si Type (x) est le même que Type (y), alors

    Renvoie le résultat de la comparaison d'égalité stricte x === y.

  4. ...

Les deux premières étapes sont la plomberie de base. Donc, en fait, la toute première étape ==est de voir si les types sont les mêmes et, si c'est le cas, de le faire à la ===place. !=et ne !==sont que des versions niées de cela.

Donc, si Flanagan a raison, cela ne NaNdonnera vrai que pour x !== x, nous pouvons être sûrs qu'il est également vrai que seul NaNdonnera vrai pour x != x.

De nombreux programmeurs JavaScript utilisent par défaut ===et !==pour éviter certains pièges autour de la contrainte de type que font les opérateurs lâches, mais il n'y a rien à lire dans l'utilisation par Flanagan de l'opérateur strict vs lâche dans ce cas.

TJ Crowder
la source
J'ai relu la 4.9.1 - Equality and Inequality Operatorssection et cela semble être la réponse. Le point clé de ===comparaison est: If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Giorgi Nakeuri
@GiorgiNakeuri: Je ne sais pas à quoi vous faites référence 4.9.1, peut-être le livre de Flanagan? Mais c'est essentiellement ce que dit la citation de la spécification ci-dessus, oui.
TJ Crowder
2
J'accepte cela car cela répond à ma question de manière formelle et précise. Merci pour les explications!
Giorgi Nakeuri
1
@Moshe: Qu'entendez-vous par "live bindings"? (Le terme n'apparaît pas dans la spécification.) Voulez-vous dire quelque chose comme l'exemple de GOTO 0aest en fait une fonction et ne renvoie pas deux fois la même valeur? Ce n'est pas la même chose qu'une valeur pour laquelle !==serait vraie, ce que le PO a demandé. C'est juste une fonction qui renvoie des valeurs différentes. foo() !== foo()n'est pas nécessairement vrai non plus, car il foopeut renvoyer des valeurs différentes à chaque appel.
TJ Crowder du
1
@Moshe Eh bien, c'est une façon super méchante de jouer avec les propriétés et les getters. Mais cela semble être à peu près le même que l'exemple de GOTO 0, juste avec une couche supplémentaire d'indirection.
JAB
37

Pour les besoins de NaN, !=et !==faites la même chose.

Cependant, de nombreux programmeurs évitent ==ou !=en JavaScript. Par exemple, Douglas Crockford les considère parmi les « mauvaises parties » du langage JavaScript car ils se comportent de manière inattendue et déroutante:

JavaScript a deux ensembles d'opérateurs d'égalité: ===et !==, et leurs jumeaux diaboliques ==et !=. Les bons fonctionnent comme vous vous attendez.

... Mon conseil est de ne jamais utiliser les mauvais jumeaux. Au lieu de cela, utilisez toujours ===et !==.

jkdev
la source
2
La question ne concerne pas NaN (malgré le titre). La question est "
TJ Crowder
@TJCrowder Deux questions, vraiment. La première question est "Est-il sûr d'utiliser les deux versions" et la réponse est que les deux versions sont équivalentes. J'aime votre réponse "sous le capot" qui explique tout en détail.
jkdev
22

Juste pour le plaisir, laissez-moi vous montrer un exemple artificiel où il xn'y en a pas, NaNmais les opérateurs se comportent de toute façon différemment. Définissez d'abord:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

Ensuite nous avons

x != x // false

mais

x !== x // true
GOTO 0
la source
9
Ha! :-) Mais c'est effectivement foo() != foo()là que foo renvoie 1 puis 2. Par exemple, les valeurs ne sont pas les mêmes, il s'agit simplement de comparer des valeurs différentes.
TJ Crowder
2

Je veux juste souligner que ce NaNn'est pas la seule chose qui produit x !== xsans utiliser l'objet global. Il existe de nombreuses façons intelligentes de déclencher ce comportement. En voici un utilisant des getters:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

Comme d'autres réponses le soulignent, ==effectue la coersion de type, mais comme dans d'autres langages et par la norme - NaN indique un échec de calcul, et pour de bonnes raisons n'est pas égal à lui-même.

Pour une raison autre que moi, les gens considèrent que c'est un problème avec JS, mais la plupart des langages qui ont des doubles (à savoir, C, Java, C ++, C #, Python et autres) présentent ce comportement exact et les gens sont très bien avec cela.

Benjamin Gruenbaum
la source
2
Oui, c'est exactement ce que @TJCrowder a mentionné dans le commentaire de la réponse de GOTO_0, n'est-ce pas?
Giorgi Nakeuri
Pourriez-vous clarifier comment obtenir la coercition de type ambigu dans ces autres langues?
chicocvenancio
0

Comme parfois, les images valent mieux que les mots, vérifiez ce tableau (ce qui est la raison pour laquelle j'en fais une réponse plutôt qu'un commentaire, c'est parce qu'il obtient une meilleure visibilité).

Là, vous pouvez voir que la comparaison d'égalité stricte (===) ne renvoie vrai que si le type et le contenu correspondent, donc

var f = "-1" === -1; //false

Alors que la comparaison d'égalité abstraite (==) vérifie uniquement le contenu * en convertissant des types puis en les comparant strictement:

var t = "-1" == -1; //true

Bien que ce ne soit pas clair, sans consulter l' ECMA , ce que JavaScript prend en compte lors de la comparaison, d'une manière que le code ci-dessous évalue à vrai.

 var howAmISupposedToKnowThat = [] == false; //true
MVCDS
la source