¿Por qué isNaN (null) == false en JS?

129

Este código en JS me da una ventana emergente que dice "Creo que nulo es un número", lo que me parece un poco inquietante. ¿Qué me estoy perdiendo?

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

Estoy usando Firefox 3. ¿Es eso un error del navegador?

Otras pruebas:

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

Entonces, ¿el problema parece no ser una comparación exacta con NaN?

Editar: Ahora que la pregunta ha sido respondida, he limpiado mi publicación para tener una mejor versión para el archivo. Sin embargo, esto hace que algunos comentarios e incluso algunas respuestas sean un poco incomprensibles. No culpes a sus autores. Entre las cosas que cambié fue:

  • Se eliminó una nota que decía que había arruinado el titular en primer lugar al revertir su significado
  • Las respuestas anteriores mostraron que no dije con suficiente claridad por qué pensaba que el comportamiento era extraño, así que agregué los ejemplos que verifican una cadena y hacen una comparación manual.
Hanno Fietz
fuente
3
¿No quieres decir "¿Por qué isNaN (null) == false"?
Matt Rogish
Según su código, isnan (nulo) devuelve falso (nulo no es "no es un número") si dice "Creo que nulo es un número".
devinmoore

Respuestas:

114

Creo que el código está tratando de preguntar, "¿es xnumérico?" con el caso específico aquí de x = null. La función isNaN()se puede usar para responder esta pregunta, pero semánticamente se refiere específicamente al valor NaN. De Wikipedia para NaN:

NaN ( N ot un N umber) es un valor del tipo de datos numérico que representa un valor indefinido o no representable, especialmente en cálculos de punto flotante.

En la mayoría de los casos, pensamos que la respuesta a "¿es numérico nulo?" debería ser no. Sin embargo, isNaN(null) == falsees semánticamente correcto, porque nullno lo es NaN.

Aquí está la explicación algorítmica:

La función isNaN(x)intenta convertir el parámetro pasado a un número 1 (equivalente a Number(x)) y luego prueba si el valor es NaN. Si el parámetro no se puede convertir a un número, Number(x)devolverá NaN2 . Por lo tanto, si la conversión del parámetro xa un número resulta NaN, devuelve verdadero; de lo contrario, devuelve falso.

Entonces, en el caso específico x = null, nullse convierte al número 0, (intente evaluar Number(null)y ver que devuelve 0,) y isNaN(0)devuelve falso. Una cadena que solo tiene dígitos puede convertirse en un número e isNaN también devuelve falso. Una cadena (p 'abcd'. Ej. ) Que no se puede convertir a un número hará isNaN('abcd')que devuelva verdadero, específicamente porque Number('abcd')devuelve NaN.

Además de estos casos extremos aparentes, hay razones numéricas estándar para devolver NaN como 0/0.

En cuanto a las pruebas de igualdad aparentemente inconsistentes que se muestran en la pregunta, el comportamiento de NaNse especifica de modo que cualquier comparación x == NaNsea ​​falsa, independientemente del otro operando, incluido él NaNmismo 1 .

Glenn Moss
fuente
8
Por cierto, NaN !== NaN. Entonces, creo que no es totalmente correcto decirlo Number('abcd') == NaNporque Number('abcd')es NaNpero no igual a NaN. Adoro JavaScript.
nilfalse
Si. Me refería a transmitir que Number('abcd')es NaN, pero he dado a entender que prueba verdadera de la igualdad, que no es el caso. Lo editaré
Glenn Moss
Me pregunto por qué isNaNy están Numberdiseñados para comportarse de esa manera.
timidboy
1
La conversión de nulla 0solo (al menos en este contexto) ocurre dentro de la isNaN()función, que coacciona su argumento.
Glenn Moss
3
Número (nulo) == 0 pero parseInt (nulo) == NaN love JS
JoshBerke
31

Me encontré con este problema yo mismo.

Para mí, la mejor manera de usar isNaN es así

isNaN(parseInt(myInt))

tomando el ejemplo de phyzome de arriba,

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]

(Alineé el resultado de acuerdo con la entrada, espero que sea más fácil de leer).

Esto me parece mejor.

chico mograbi
fuente
1
No funcionará si myInt= "123d". parseIntconvierte "123d" a 123, que luego falla la isNaNprueba.
Divesh premdeep
de hecho, si quieres ver ese escenario también, sugiero combinar mi respuesta y la de Glenn. que se verá así isNaN(parseInt(str,10)) || isNaN(Number()). por cierto, para mí, ya que tengo que ejecutar parseIntpara usar el valor numérico de la cadena, lo que permite que "123d" sea considerado como un número válido está bien. Sin embargo, veo la necesidad de detectar ese escenario también.
chico mograbi
8

(Mi otro comentario tiene un enfoque práctico. Aquí está el lado teórico).

Busqué el estándar ECMA 262 , que es lo que implementa Javascript. Su especificación para isNan:

Aplica ToNumber a su argumento, luego devuelve verdadero si el resultado es NaN y, de lo contrario, devuelve falso.

La sección 9.3 especifica el comportamiento de ToNumber(que no es una función invocable, sino un componente del sistema de conversión de tipos). Para resumir la tabla, ciertos tipos de entrada pueden producir un NaN. Estos son tipo undefined, tipo number(pero solo el valor NaN), cualquier objeto cuya representación primitiva sea NaN, y cualquier objeto stringque no pueda analizarse. Este hojas undefined, NaN, new Number(NaN), y la mayoría de las cadenas.

Cualquier entrada de este tipo que se produzca NaNcomo salida cuando se pase a ToNumberproducirá un truecuando se alimente a isNaN. Como nullpuede convertirse con éxito en un número, no produce true.

Y es por eso.

trata bien tus modificaciones
fuente
7

Esto es realmente inquietante. Aquí hay una serie de valores que probé:

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

Evalúa (en la consola Firebug) para:

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

Cuando llamo x.map(isNaN)(para llamar a isNaN en cada valor), obtengo:

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

En conclusión, ¡ isNaNparece bastante inútil! ( Editar : excepto que resulta que isNaN solo se define sobre Number, en cuyo caso funciona bien, solo con un nombre engañoso).

Por cierto, aquí están los tipos de esos valores:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number
trata bien tus modificaciones
fuente
¿Qué imaginas que debería significar NaN? ¿Qué crees que es tan engañoso sobre NaN o al probarlo? ¿Por qué estás perturbado?
Bekim Bacaj
Bueno, esto fue hace 8 años, pero parece que me molestó que 1) tenga resultados inconsistentes para valores que no son del tipo Número, y 2) siempre devuelve verdadero para algo que no es del tipo Número. Porque una cadena no es, de hecho, un NaN. (También vea mi otra respuesta, que explica por qué sucede esto).
trate bien sus modificaciones el
5

Nulo no es NaN, así como una cadena no es NaN. isNaN () solo prueba si realmente tienes el objeto NaN.

artilugio
fuente
Pero al menos una cadena se convierte en un objeto NaN, ya que isNaN ("texto") devuelve verdadero.
Hanno Fietz
1

No estoy exactamente seguro cuando se trata de JS, pero he visto cosas similares en otros idiomas y generalmente es porque la función solo está verificando si nulo es exactamente igual a NaN (es decir, nulo === NaN sería falso). En otras palabras, no es que piense que nulo es de hecho un número, sino que nulo no es NaN. Esto se debe probablemente a que ambos están representados de manera diferente en JS para que no sean exactamente iguales, de la misma manera que 9! == '9'.

Jason Tennier
fuente
1

En ES5, se define como isNaN (number)devuelve verdadero si el argumento coacciona a NaN, y de lo contrario devuelve falso.

Y vea la tabla de conversión La operación abstracta ToNumber . Entonces, internamente, la evaluación del motor js ToNumber(Null)es +0, luego eventualmente isNaN(null)esfalse

rab
fuente
0

Nota:

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

El operador == realiza conversión de tipo, mientras que === no.

El sitio web de Douglas Crockford , un Yahoo! Evangelista de JavaScript, es un gran recurso para cosas como esta.

cllpse
fuente