¿Por qué isNaN ("") (cadena con espacios) es igual a falso?


160

En JavaScript, ¿por qué isNaN(" ")evaluar a false, pero isNaN(" x")evaluar a true?

Estoy realizando operaciones numérico en un campo de entrada de texto, y yo estoy verificando si el campo es null, ""o NaN. Cuando alguien escribe un puñado de espacios en el campo, mi validación falla en los tres, y estoy confundido sobre por qué pasa el isNaNcontrol.


1
Hm ... no estoy muy seguro de dónde fue la otra mitad del tema. Se supone que dice "JavaScript: ¿Por qué isNaN (" ") se evalúa como falso?"
IVR Avenger

Jes, ese es el comportamiento (vacío o el espacio devuelve falso para isNaN), pero no encontré las especificaciones exactas de esta función.
Lucero


¡Javascript en estos temas parece vudú! Nunca se sabe y la explicación siempre es bastante compleja. "" == false // trueyisNaN(" ") // false
João Pimentel Ferreira el

Respuestas:


155

JavaScript interpreta una cadena vacía como un 0, que luego falla la prueba isNAN. Puede usar primero parseInt en la cadena que no convertirá la cadena vacía a 0. El resultado debería fallar isNAN.


53
¡Pero parseInt ("123abcd") devuelve 123, lo que significa que isNaN (parseInt ("123abcd")) devolverá falso mientras que debería devolver verdadero!
Pawan Nogariya

11
Entonces, ¿qué tal (IsNaN (string) || isNaN (parseInt (string)))
mate

55
Hay 2 pasos en la interpretación isNaN(arg). 1) Convertir arg en número, 2) Verificar si ese número es el valor numérico NaN. Eso me ayudó a entenderlo mejor.
xdhmoore

3
@ Antonio_Haley Espera un momento, no lo entiendo. Si "JavaScript interpreta una cadena vacía como un 0", ¿por qué parseInt ("") devuelve NaN?
Jean-François Beauchamp

1
@ Jean-François Tienes razón, la afirmación más correcta sería "isNaN interpreta una cadena vacía como 0", no JavaScript en sí.
Antonio Haley

82

Puede encontrar esto sorprendente o tal vez no, pero aquí hay un código de prueba para mostrarle la locura del motor de JavaScript.

document.write(isNaN("")) // false
document.write(isNaN(" "))  // false
document.write(isNaN(0))  // false
document.write(isNaN(null)) // false
document.write(isNaN(false))  // false
document.write("" == false)  // true
document.write("" == 0)  // true
document.write(" " == 0)  // true
document.write(" " == false)  // true
document.write(0 == false) // true
document.write(" " == "") // false

entonces esto significa que

" " == 0 == false

y

"" == 0 == false

pero

"" != " "

Que te diviertas :)


55
+1 Gran publicación. ¿Puedes agregar cómo encaja el operador triple igual (=== y! ==) aquí?
bendewey 05 de

2
Deberías probar NaN === NaN o NaN == NaN ;-) No sé si todo esto significa que el motor de JavaScript es una locura o que JavaScript es malo para los programadores locos.
KooiInc 05 de

10
@Kooilnc el hecho de que NaN! = NaN es, en realidad, una buena opción por una vez. La idea es que NaN casi siempre es el resultado de un cálculo que fue diferente a la forma en que el programador pretendía, y asumir que los resultados de dos cálculos que salieron "mal" son iguales es bastante peligroso, diría yo.
skrebbel

2
@Kooilnc no quita ni un poco la debilidad de javascript, pero estos NaN simplemente obedecen el estándar de coma flotante IEEE 754. Puedes leer TODO sobre esto como de costumbre en la gran W: en.wikipedia.org/wiki/NaN
Spike0xff

@NickBerardi F'ing LOL! Estoy tan contenta de haber visto esta publicación. Me ayudó a descubrir por qué la función isNaN está tan retrasada. Lo eliminaré de mi código no completamente desarrollado en este momento, y es probable que nunca lo vuelva a usar. Voy a validar para null, ""y " "yo mismo. ¡Gracias!
VoidKing

16

Para entenderlo mejor, abra el pdf de especificaciones de Ecma-Script en la página 43 "ToNumber Applied to the String Type"

Si una cadena tiene una sintaxis numérica, que puede contener cualquier número de caracteres de espacio en blanco, se puede convertir a tipo de número. La cadena vacía se evalúa a 0. También la cadena 'Infinito' debería dar

isNaN('Infinity'); // false

13

Intenta usar:

alert(isNaN(parseInt("   ")));

O

alert(isNaN(parseFloat("    ")));

3
hola señor, isNaN (parseInt ("123a")): devolverá 123 por lo que su solución no funcionará, si la cadena contiene aplha numérico
Sajjad Ali Khan

6

Por la MDNrazón del problema que enfrenta

Cuando el argumento de la función isNaN no es del tipo Número, el valor se convierte primero en un Número. El valor resultante se prueba para determinar si es NaN.

Es posible que desee verificar la siguiente respuesta integral que cubre la comparación NaN para la igualdad también.

Cómo probar si una variable de JavaScript es NaN


5

Creo que es por la tipificación de Javascript: ' 'se convierte a cero, mientras 'x'que no lo es:

alert(' ' * 1); // 0
alert('x' * 1); // NaN

4

Si desea implementar una función isNumber precisa, esta es una forma de hacerlo desde Javascript: The Good Parts de Douglas Crockford [página 105]

var isNumber = function isNumber(value) {
   return typeof value === 'number' && 
   isFinite(value);
}

44
@Xyan en cuyo caso esta función no es muy útil para realizar la tarea que el OP estaba pidiendo, que era inspeccionar una representación de cadena de un número ...
ErikE

usar el llamado operador de igualdad estricta de cualquier valor dado contra un literal de cadena como "número" es estúpido,
Bekim Bacaj

4

La respuesta no completamente correcta

La respuesta altamente votada y aceptada de Antonio Haley aquí supone erróneamente que este proceso pasa por la parseIntfunción de JavaScript :

Puede usar parseInt en la cadena ... El resultado debería fallar isNAN.

Podemos refutar fácilmente esta declaración con la cadena "123abc":

parseInt("123abc")    // 123     (a number...
isNaN("123abc")       // true     ...which is not a number)

Con esto, podemos ver que la parseIntfunción de JavaScript regresa "123abc"como el número 123, pero su isNaNfunción nos dice que "123abc" no es un número.

La respuesta correcta

ECMAScript-262 define cómo funciona la isNaNverificación en la sección 18.2.3 .

18.2.3 isNaN(Número)

La isNaNfunción es el %isNaN%objeto intrínseco. Cuando isNaNse llama a la función con un número de argumento, se toman los siguientes pasos:

  1. Dejar numser? ToNumber(number).
  2. Si numes así NaN, regrese true.
  3. De lo contrario, regrese false.

La ToNumberfunción a la que hace referencia también se define en la sección 7.1.3 de ECMAScript-262 . Aquí, nos dicen cómo JavaScript maneja las cadenas que se pasan a esta función.

El primer ejemplo dado en la pregunta es una cadena que no contiene nada más que espacios en blanco. Esta sección establece que:

A StringNumericLiteralque está vacío o contiene solo espacio en blanco se convierte +0.

Por " "lo tanto, la cadena de ejemplo se convierte en +0, que es un número.

La misma sección también establece:

Si la gramática no puede interpretar el Stringcomo una expansión de StringNumericLiteral, entonces el resultado de ToNumberes NaN.

Sin citar todas las verificaciones contenidas en esa sección, el " x"ejemplo dado en la pregunta cae en la condición anterior ya que no puede interpretarse como a StringNumericLiteral. " x"por lo tanto se convierte a NaN.


2

No estoy seguro de por qué , pero para solucionar el problema, siempre puede recortar espacios en blanco antes de verificar. Probablemente quieras hacer eso de todos modos.


44
una cadena vacía recortada también falla la prueba isNaN.
Egemenk

2

La función isNaN("")realiza una coerción de tipo Cadena a Número

ECMAScript 3-5 define los siguientes valores de retorno para el operador typeof:

  • indefinido
  • objeto (nulo, objetos, matrices)
  • booleano
  • número
  • cuerda
  • función

Es mejor envolver nuestra prueba en un cuerpo de función:

function isNumber (s) {
    return typeof s == 'number'? true
           : typeof s == 'string'? (s.trim() === ''? false : !isNaN(s))
           : (typeof s).match(/object|function/)? false
           : !isNaN(s)
}

Esta función no tiene la intención de probar el tipo de variable , sino que prueba el valor forzado . Por ejemplo, los booleanos y las cadenas se convierten en números, por lo que quizás desee llamar a esta función comoisNumberCoerced()

si no hay necesidad de probar otros tipos que no sean cadena y número , entonces el siguiente fragmento podría usarse como parte de alguna condición:

if (!isNaN(s) && s.toString().trim()!='') // 's' can be boolean, number or string
    alert("s is a number")

1

Le sugiero que use la siguiente función si realmente desea una verificación adecuada si es un número entero:

function isInteger(s)
{
   return Math.ceil(s) == Math.floor(s);
}

1

Eso isNaN(" ")es falso es parte del comportamiento confuso de la isNaNfunción global debido a su coerción de no números a un tipo numérico.

De MDN :

Desde las primeras versiones de la isNaNespecificación de la función, su comportamiento para los argumentos no numéricos ha sido confuso. Cuando el argumento de la isNaNfunción no es del tipo Número, el valor se convierte primero en un Número. El valor resultante se prueba para determinar si es así NaN. Por lo tanto, para los no números que cuando se convierten al tipo numérico dan como resultado un valor numérico no NaN válido (especialmente la cadena vacía y las primitivas booleanas, que cuando se coaccionan dan valores numéricos cero o uno), el valor devuelto "falso" puede ser inesperado; la cadena vacía, por ejemplo, seguramente "no es un número".

Tenga en cuenta también que con ECMAScript 6, ahora también existe el Number.isNaNmétodo, que según MDN:

En comparación con la isNaN()función global , Number.isNaN()no sufre el problema de convertir forzosamente el parámetro a un número. Esto significa que ahora es seguro pasar valores que normalmente se convertirían NaN, pero que en realidad no son el mismo valor que NaN. Esto también significa que solo los valores del número de tipo, que también son NaN, devuelven true.

Por desgracia :

Incluso el Number.isNaNmétodo ECMAScript 6 tiene sus propios problemas, como se describe en la publicación del blog: solución del feo problema de NaN de JavaScript y ES6 .


1

La isNaNfunción espera un Número como argumento, por lo que los argumentos de cualquier otro tipo (en su caso, una cadena) se convertirán en Número antes de que se realice la lógica de la función real. (¡Tenga en cuenta que NaNtambién es un valor de tipo Number!)

Por cierto. Esto es común para todas las funciones integradas: si esperan un argumento de cierto tipo, el argumento real se convertirá utilizando las funciones de conversión estándar. Hay conversiones estándar entre todos los tipos básicos (bool, string, number, object, date, null, undefined).

La conversión estándar para Stringto Numberse puede invocar explícitamente con Number(). Entonces podemos ver que:

  • Number(" ") evalúa a 0
  • Number(" x") evalúa a NaN

Dado esto, ¡el resultado de la isNaNfunción es completamente lógico!

La verdadera pregunta es por qué la conversión estándar de Cadena a Número funciona como lo hace. La conversión de cadena a número realmente está destinada a convertir cadenas numéricas como "123" o "17.5e4" a los números equivalentes. La conversión primero omite el espacio en blanco inicial (por lo que "123" es válido) y luego trata de analizar los restos como un número. Si no se puede analizar como un número ("x" no lo es), entonces el resultado es NaN. Pero existe la regla especial explícita de que una cadena que está vacía o solo un espacio en blanco se convierte a 0. Entonces, esto explica la conversión.

Referencia: http://www.ecma-international.org/ecma-262/5.1/#sec-9.3.1


1

Escribí esta pequeña función rápida para ayudar a resolver este problema.

function isNumber(val) {
     return (val != undefined && val != null && val.toString().length > 0 && val.toString().match(/[^0-9\.\-]/g) == null);
};

Simplemente verifica si hay caracteres que no sean numéricos (0-9), que no sean '-' o '.', Y que no estén indefinidos, nulos o vacíos y devuelva verdadero si no hay coincidencias. :)


Un tardío gracias por esto; Esto resolvió mi problema muy bien.
Henry

1

Como se explicó anteriormente, la isNaNfunción forzará la cadena vacía a un número antes de validarla, cambiando así una cadena vacía a 0 (que es un número válido). Sin embargo, descubrí que la parseIntfunción volverá NaNcuando intente analizar una cadena vacía o una cadena con solo espacios. Como tal, la siguiente combinación parece estar funcionando bien:

if ( isNaN(string) || isNaN(parseInt(string)) ) console.log('Not a number!');

Esta verificación funcionará para números positivos, números negativos y números con un punto decimal, por lo que creo que cubre todos los casos numéricos comunes.


1

NaN ! == "no es un número"

NaN es un valor de tipo de número

esta es una definición de isNaN () en ECMAScript

1. Let num be ToNumber(number).
2. ReturnIfAbrupt(num).
3. If num is NaN, return true.
4. Otherwise, return false.

Intenta convertir cualquier valor a Número.

Number(" ") // 0
Number("x") // NaN
Number(null) // 0

Si desea determinar si el valor es NaN, primero debe intentar convertirlo en un valor de Número.


0

Esta función parecía funcionar en mis pruebas.

function isNumber(s) {
    if (s === "" || s === null) {
        return false;
    } else {
        var number = parseInt(s);
        if (number == 'NaN') {
            return false;
        } else {
            return true;
        }
    }
}

2
Toda su función se puede escribir:return !(s === "" || s === null || parseInt(s) == 'NaN');
ErikE

0

Qué pasa

function isNumberRegex(value) {        
    var pattern = /^[-+]?\d*\.?\d*$/i;
    var match = value.match(pattern);
    return value.length > 0 && match != null;
}

0

La función isNaN incorporada de JavaScript es, como debería esperarse por defecto, un "Operador de tipo dinámico". Por lo tanto, todos los valores que (durante el proceso DTC) pueden producir un verdadero simple | falso como , no puede ser NaN."", " ", " 000"

Lo que significa que el argumento suministrado primero se someterá a una conversión como en:

function isNaNDemo(arg){
   var x = new Number(arg).valueOf();
   return x != x;
}

Explicación:

En la línea superior del cuerpo de la función, (primero) estamos tratando de convertir con éxito el argumento en un objeto numérico. Y (segundo), utilizando el operador de punto, estamos, para nuestra conveniencia, quitando inmediatamente el valor primitivo del objeto creado.

En la segunda línea, estamos tomando el valor obtenido en el paso anterior, y la ventaja del hecho de que NaN no es igual a nada en el universo, ni siquiera a sí mismo, por ejemplo: NaN == NaN >> falsepara finalmente compararlo (por desigualdad) consigo mismo .

De esta manera, la función return solo será verdadera cuando, y solo si, el argumento-return proporcionado, es un intento fallido de conversión a un objeto numérico, es decir, un número que no es un número; por ejemplo, NaN.


isNaNstatic ()

Sin embargo, para un operador de tipo estático, si es necesario y cuando es necesario, podemos escribir una función mucho más simple como:

function isNaNstatic(x){   
   return x != x;
}

Y evite el DTC por completo, de modo que si el argumento no es explícitamente un número NaN, devolverá falso. Por lo tanto, prueba contra lo siguiente:

isNaNStatic(" x"); // will return false porque sigue siendo una cuerda.

Sin embargo: isNaNStatic(1/"x"); // will of course return true.como será por ejemplo isNaNStatic(NaN); >> true.

Pero al contrario isNaN, isNaNStatic("NaN"); >> falseporque (el argumento) es una cadena ordinaria.

ps: La versión estática de isNaN puede ser muy útil en escenarios de codificación modernos. Y bien puede ser una de las principales razones por las que me tomé mi tiempo para publicar esto.

Saludos.


0

isNAN(<argument>)es una función para saber si un argumento dado es un número ilegal. isNaNconvierte los argumentos en Tipo de número. Si desea verificar si el argumento es numérico o no? Utilice la $.isNumeric()función en jQuery.

Es decir, isNaN (foo) es equivalente a isNaN (Number (foo)) Acepta cualquier cadena que tenga todos los números como números por razones obvias. Por ej.

isNaN(123) //false
isNaN(-1.23) //false
isNaN(5-2) //false
isNaN(0) //false
isNaN('123') //false
isNaN('Hello') //true
isNaN('2005/12/12') //true
isNaN('') //false
isNaN(true) //false
isNaN(undefined) //true
isNaN('NaN') //true
isNaN(NaN) //true
isNaN(0 / 0) //true

0

yo uso esto

    function isNotANumeric(val) {
    	if(val.trim && val.trim() == "") {
         return true;
      } else {
      	 return isNaN(parseFloat(val * 1));
      }
    }
    
    alert(isNotANumeric("100"));  // false
    alert(isNotANumeric("1a"));   // true
    alert(isNotANumeric(""));     // true
    alert(isNotANumeric("   "));  // true


0

Al comprobar si determinado valor de cadena con espacios en blanco o " "se isNaNpuede que tratar de realizar la validación de cadena, ejemplo:

// value = "123 " if (value.match(/\s/) || isNaN(value)) { // do something }

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.