Pourquoi isNaN (null) == false dans JS?


129

Ce code dans JS me donne un popup disant "je pense que nul est un nombre", ce que je trouve légèrement dérangeant. Qu'est-ce que je rate?

if (isNaN(null)) {
  alert("null is not a number");
} else {
  alert("i think null is a number");
}

J'utilise Firefox 3. Est-ce un bug du navigateur?

Autres tests:

console.log(null == NaN);   // false
console.log(isNaN("text")); // true
console.log(NaN == "text"); // false

Donc, le problème ne semble pas être une comparaison exacte avec NaN?

Edit: Maintenant que la question a été répondue, j'ai nettoyé mon message pour avoir une meilleure version pour l'archive. Cependant, cela rend certains commentaires et même certaines réponses un peu incompréhensibles. Ne blâmez pas leurs auteurs. Parmi les choses que j'ai changées, il y avait:

  • Suppression d'une note disant que j'avais foiré le titre en premier lieu en inversant sa signification
  • Les réponses précédentes ont montré que je n'indiquais pas assez clairement pourquoi je pensais que le comportement était étrange, j'ai donc ajouté les exemples qui vérifient une chaîne et font une comparaison manuelle.

3
ne voulez-vous pas dire "Pourquoi isNaN (null) == false"?
Matt Rogish

En fonction de votre code, isnan (null) renvoie false (null n'est pas «pas un nombre») s'il dit «je pense que nul est un nombre».
devinmoore le

Réponses:


114

Je crois que le code essaie de demander "est-ce xnumérique?" avec le cas spécifique ici de x = null. La fonction isNaN()peut être utilisée pour répondre à cette question, mais sémantiquement, elle se réfère spécifiquement à la valeur NaN. De Wikipedia pour NaN:

NaN ( N ot a N umber) est une valeur du type de données numérique représentant une valeur indéfinie ou non représentable, en particulier dans les calculs à virgule flottante.

Dans la plupart des cas, nous pensons que la réponse à "est-ce qu'une valeur numérique nulle?" devrait être non. Cependant, isNaN(null) == falseest sémantiquement correct, car ce nulln'est pas le cas NaN.

Voici l'explication algorithmique:

La fonction isNaN(x)tente de convertir le paramètre passé en un nombre 1 (équivalent à Number(x)), puis teste si la valeur est NaN. Si le paramètre ne peut pas être converti en nombre, Number(x)renvoie NaN2 . Par conséquent, si la conversion d'un paramètre xen un nombre aboutit NaN, elle renvoie true; sinon, il renvoie false.

Donc, dans le cas spécifique x = null, nullest converti en nombre 0, (essayez d'évaluer Number(null)et voyez qu'il renvoie 0,) et isNaN(0)retourne false. Une chaîne composée uniquement de chiffres peut être convertie en nombre et isNaN renvoie également false. Une chaîne (par exemple 'abcd') qui ne peut pas être convertie en nombre provoquera isNaN('abcd')le retour de true, en particulier parce que Number('abcd')renvoie NaN.

En plus de ces cas de bord apparents, il y a les raisons numériques standard pour renvoyer NaN comme 0/0.

Quant aux tests d'égalité apparemment incohérents indiqués dans la question, le comportement de NaNest spécifié de telle sorte que toute comparaison x == NaNest fausse, quel que soit l'autre opérande, y compris NaNlui-même 1 .


8
BTW, NaN !== NaN. Donc, je pense qu'il n'est pas tout à fait correct de dire Number('abcd') == NaNparce que Number('abcd')c'est NaNmais pas égal à NaN. J'adore JavaScript.
nilfalse

Oui. Je voulais dire que Number('abcd')c'est, NaNmais j'ai laissé entendre que cela testait l'égalité, ce qui n'est pas le cas. Je vais le modifier.
Glenn Moss

Je me demande pourquoi sont isNaNet Numberconçus pour se comporter de cette façon?
timidboy

1
La conversion de nullen 0uniquement (du moins dans ce contexte) se produit dans la isNaN()fonction, ce qui contraint son argument.
Glenn Moss

3
Number (null) == 0 but parseInt (null) == NaN love JS
JoshBerke

31

Je viens de rencontrer ce problème moi-même.

Pour moi, la meilleure façon d'utiliser isNaN est comme ça

isNaN(parseInt(myInt))

en prenant l'exemple de phyzome d'en haut,

var x = [undefined, NaN,     'blah', 0/0,  null, 0,     '0',   1,     1/0, -1/0,  Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
        [true,      true,    true,   true, true, false, false, false, true, true, false]

(J'ai aligné le résultat en fonction de l'entrée, j'espère que cela le rendra plus facile à lire.)

Cela me semble mieux.


1
Ne fonctionnera pas si myInt= "123d". parseIntconvertit «123d» en 123, qui échoue alors au isNaNtest.
plongée premdeep

en effet, si vous voulez également saisir ce scénario, je suggère de combiner ma réponse et celle de Glenn. qui ressemblera à ceci isNaN(parseInt(str,10)) || isNaN(Number()). btw - pour moi, puisque je dois courir parseIntpour utiliser la valeur numérique de la chaîne, permettre à "123d" d'être considéré comme un nombre valide est très bien. Cependant, je vois la nécessité de détecter ce scénario également.
guy mograbi

8

(Mon autre commentaire adopte une approche pratique. Voici le côté théorique.)

J'ai recherché la norme ECMA 262 , qui est ce que Javascript implémente. Leur spécification pour isNan:

Applique ToNumber à son argument, puis renvoie true si le résultat est NaN, et renvoie sinon false.

La section 9.3 spécifie le comportement de ToNumber(qui n'est pas une fonction appelable, mais plutôt un composant du système de conversion de type). Pour résumer le tableau, certains types d'entrée peuvent produire un NaN. Ce sont le type undefined, le type number(mais seulement la valeur NaN), tout objet dont la représentation primitive est NaN, et tout objet stringqui ne peut pas être analysé. Cette feuille undefined, NaN, new Number(NaN), et la plupart des chaînes.

Toute entrée de ce type qui produit NaNune sortie lorsqu'elle est transmise à ToNumberproduira une truelorsqu'elle est transmise à isNaN. Puisqu'il nullpeut être converti avec succès en un nombre, il ne produit pas true.

Et c'est pourquoi.


7

C'est vraiment inquiétant. Voici un tableau de valeurs que j'ai testé:

var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]

Il évalue (dans la console Firebug) à:

,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5

Quand j'appelle x.map(isNaN)(pour appeler isNaN sur chaque valeur), j'obtiens:

true,true,true,true,false,false,false,false,false,false,false

En conclusion, isNaNça a l'air assez inutile! ( Edit : Sauf qu'il s'avère que isNaN n'est défini que sur Number, auquel cas cela fonctionne très bien - juste avec un nom trompeur.)

Incidemment, voici les types de ces valeurs:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number

Que pensez-vous que NaN devrait signifier? À votre avis, qu'est-ce qui est si trompeur à propos de NaN ou lors des tests? Pourquoi es-tu dérangé?
Bekim Bacaj

Eh bien, c'était il y a 8 ans, mais il semble que j'ai été dérangé par le fait que 1) il a des résultats incohérents pour les valeurs qui ne sont pas de type Number, et 2) il retourne jamais vrai pour quelque chose qui n'est pas de type Number. Parce qu'une chaîne n'est pas, en fait, un NaN. (Voir aussi mon autre réponse, qui explique pourquoi cela se produit.)
Traitez bien vos mods

5

Null n'est pas NaN, et une chaîne n'est pas NaN. isNaN () teste simplement si vous avez vraiment l'objet NaN.


Mais alors au moins une chaîne est convertie en un objet NaN, car isNaN ("text") renvoie true.
Hanno Fietz le

1

Je ne sais pas exactement quand il s'agit de JS mais j'ai vu des choses similaires dans d'autres langues et c'est généralement parce que la fonction vérifie uniquement si null est exactement égal à NaN (c'est-à-dire que null === NaN serait faux). En d'autres termes, ce n'est pas qu'il pense que null est en fait un nombre, mais c'est plutôt que null n'est pas NaN. C'est probablement parce que les deux sont représentés différemment dans JS afin qu'ils ne soient pas exactement égaux, de la même manière que 9! == '9'.



0

Remarque:

"1" == 1 // true
"1" === 1 // false

L'opérateur == effectue une conversion de type, contrairement à ===.

Le site Web de Douglas Crockford , un site Yahoo! JavaScript evangelist, est une excellente ressource pour des trucs comme ça.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.