¿Por qué isNaN ("") es igual a falso
-
05-07-2019 - |
Pregunta
Tengo una pregunta rápida (¡espero!). En JS, ¿por qué isNaN (" ")
se evalúa como falso, pero isNaN (" x ")
se evalúa como verdadero?
Estoy realizando operaciones numéricas en un campo de entrada de texto y estoy comprobando si el campo es nulo, " " ;, o NaN. Cuando alguien escribe un puñado de espacios en el campo, mi validación falla en los tres, y estoy confundido en cuanto a por qué se pasa la verificación de ISN.
¡Gracias!
Solución
JavaScript interpreta una cadena vacía como un 0, que luego falla la prueba isNAN. Puede usar parseInt en la cadena primero que no convertirá la cadena vacía a 0. El resultado debería fallar isNAN.
Otros consejos
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
así que esto significa que
" " == 0 == false
y
"" == 0 == false
pero
"" != " "
Diviértete :)
Para comprenderlo mejor, abra Ecma-Script spec pdf en la página 43 " ToNumber aplicado al tipo de cadena "
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
Intenta usar:
alert(isNaN(parseInt(" ")));
O
alert(isNaN(parseFloat(" ")));
De MDN
razó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.
Creo que se debe a la escritura de Javascript: ''
se convierte a cero, mientras que 'x'
no es:
alert(' ' * 1); // 0
alert('x' * 1); // NaN
Si desea implementar una función isNumber precisa, esta es una forma de hacerlo desde Javascript: The Good Parts por Douglas Crockford [página 105]
var isNumber = function isNumber(value) {
return typeof value === 'number' &&
isFinite(value);
}
La respuesta no totalmente correcta
respuesta altamente aceptada y aceptada aquí hace una suposición errónea de que este proceso pasa por el código de JavaScript
function:
Puedes 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 función parseInt
de JavaScript devuelve " 123abc "
como el número 123
, sin embargo, su esNaN La función
nos dice que " 123abc "
no es un número.
La respuesta correcta
ECMAScript-262 define cómo funciona la verificación isNaN
en sección 18.2.3 .
18.2.3
isNaN
(Número)La función
isNaN
es el objeto intrínseco% isNaN%
. Cuando se llama a la funciónisNaN
con un número de argumento, se toman los siguientes pasos:
- Sea
num
?ToNumber (número)
.- Si
num
esNaN
, devuelvetrue
.- De lo contrario, devuelve
false
.
La función ToNumber
a la que hace referencia también se define en ECMAScript- 262's sección 7.1.3 . 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 solo contiene caracteres de espacio en blanco. Esta sección establece que:
Un
StringNumericLiteral
que está vacío o que solo contiene espacios en blanco se convierte a+0
.
El " "
la cadena de ejemplo se convierte a +0
, que es un número.
La misma sección también dice:
Si la gramática no puede interpretar
String
como una expansión deStringNumericLiteral
, el resultado deToNumber
esNaN
.
Sin citar todos los controles incluidos en esa sección, el " El ejemplo de x "
dado en la pregunta cae en la condición anterior, ya que no se puede interpretar como un StringNumericLiteral
. NaN
.
No estoy seguro de por qué , pero para solucionar el problema, siempre puede recortar los espacios en blanco antes de realizar la comprobación. Probablemente quieras hacer eso de todos modos.
La función isNaN (" ")
realiza una Cadena a Número coerción de tipo
ECMAScript 3-5 define los siguientes valores de retorno para el Tipo de operador:
- indefinido
- objeto (nulo, objetos, matrices)
- booleano
- número
- cadena
- función
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 la variable tipo , sino que prueba el valor coaccionado . Por ejemplo, los booleanos y las cadenas se convierten en números, por lo que quizás desee llamar a esta función como isNumberCoerced()
si no hay necesidad de probar 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")
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);
}
Que isNaN (" " ")
es falso es parte del comportamiento confuso de la función global isNaN
debido a su coacción de no números a un tipo numérico .
De MDN :
Desde las primeras versiones de la especificación de la función
isNaN
, su comportamiento para los argumentos no numéricos ha sido confuso. Cuando el argumento de la funciónisNaN
no es de tipo Number, el valor se convierte primero en un Number. El valor resultante se prueba para determinar si esNaN
. Por lo tanto, para los no números que, cuando se los obliga a obtener un tipo numérico, se obtiene un valor numérico no NaN válido (en particular, la cadena vacía y los primitivos booleanos, que cuando son obligados dan valores numéricos de cero o uno), el " falso " el valor devuelto puede ser inesperado; la cadena vacía, por ejemplo, seguramente no es un número. "
También tenga en cuenta que con ECMAScript 6, ahora también existe el método Number.isNaN
, que según MDN:
En comparación con la función global
isNaN ()
,Number.isNaN ()
no tiene el problema de convertir a la fuerza el parámetro en un número. Esto significa que ahora es seguro pasar valores que normalmente se convertirían aNaN
, pero en realidad no tienen el mismo valor queNaN
. Esto también significa que solo los valores del número de tipo, que también sonNaN
, devuelventrue
.
Desafortunadamente :
Incluso el método Number.isNaN
de ECMAScript 6 tiene sus propios problemas, como se describe en la publicación del blog - Solución del problema feo de JavaScript y ES6 NaN .
Como se explicó anteriormente, la función isNaN
coaccionará 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 función parseInt
devolverá NaN
cuando 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 ('¡No es un número!');
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.
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;
}
}
}
¿Qué pasa con
?function isNumberRegex(value) {
var pattern = /^[-+]?\d*\.?\d*$/i;
var match = value.match(pattern);
return value.length > 0 && match != null;
}
La función isNaN
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 la lógica de la función real es interpretado. (¡Tenga en cuenta que NaN
también es un valor de tipo Número!)
Por cierto. esto es común para todas funciones incorporadas: si esperan un argumento de cierto tipo, el argumento real se convertirá usando 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 de String
a Number
se puede invocar explícitamente con Number ()
. Entonces podemos ver eso:
-
Number (" ")
se evalúa como0
-
El número (" x ")
se evalúa comoNaN
Dado esto, ¡el resultado de la función isNaN
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 está realmente destinada a convertir cadenas numéricas como " 123 " o "17.5e4" a los números equivalentes. La conversión primero omite los espacios en blanco iniciales (por lo que " 123 " es válido) y luego intenta 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
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 busca cualquier carácter que no sea numérico (0-9), que no sea '-' o '.', y que no esté indefinido, nulo o vacío y devuelva verdadero si no hay coincidencias. :)
La función JavaScript isNaN incorporada es, como debería esperarse de forma predeterminada, un " Operador de tipo dinámico " ;.
Por lo tanto, todos los valores que (durante el proceso DTC) pueden producir un valor simple | falso como " " ;, " " ;, " 000 "
, no puede ser NaN.
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), usando el operador de punto, estamos, para nuestra conveniencia, quitando inmediatamente, el primitivo valor 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 para sí mismo, por ejemplo: NaN == NaN > > falso
para finalmente compararlo (por desigualdad) consigo mismo.
De esta forma, la función return arrojará true solo 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 "); // devolverá falso
porque sigue siendo una cadena.
Sin embargo:
isNaNStatic (1 / " x "); // por supuesto devolverá true.
como lo hará, por ejemplo, isNaNStatic (NaN); > > verdadero
.
Pero a diferencia de isNaN
, el isNaNStatic (" NaN "); > > false
porque (el argumento) es una cadena normal.
p.s .: 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.
isNAN (< argumento >)
es una función para determinar si un argumento dado es un número ilegal.
isNaN
tipea los argumentos en el tipo de Número. Si desea verificar si el argumento es numérico o no? Utilice la función $ .isNumeric ()
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
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
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 Número.
Al verificar si cierto valor de cadena con espacios en blanco o " "
es isNaN
tal vez intente realizar la validación de la cadena, por ejemplo:
// value = " 123 "
if (value.match (/ \ s /) || isNaN (value)) {
// hacer algo
}