Underscore: _.isNumber (NaN) devuelve verdadero

Creado en 15 dic. 2011  ·  38Comentarios  ·  Fuente: jashkenas/underscore

Como NaN significa "No es un número", la verificación isNumber en este caso parece que debería devolver falso. Observo en otras discusiones que, de hecho, esto es a propósito. Quizás la documentación debería reflejar este hecho explícitamente.

enhancement fixed

Comentario más útil

Ok, comencé con un problema simple. Tengo entradas que son cadenas, números y posiblemente NaN. Tengo una solución simple en JavaScript base que utiliza parseInt e isFinite para probar si el parse int tiene éxito. Simple pero no completamente limpio o autodescriptivo de mi objetivo. Entonces, decido usar mi increíble biblioteca goto para hacer este tipo de tareas. En la inspección inicial encuentro una función que dice que toma un valor y le dice si es un número, que según mi definición común es lo que creo que quiero.

Primero, con esto, ¿puede ver cómo agregar una línea a la documentación ayudaría a los usuarios de la biblioteca a obtener la función correcta para el trabajo la primera vez? En segundo lugar, pido nuevamente un ejemplo en el que esto sea significativo.

Parece que sabes todo lo que hay que saber sobre el idioma. Por lo tanto, en lugar de lanzar soluciones a ciegas en la oscuridad que mi hermano de octavo grado podría darme, tal vez sería bueno si pudieras usar algo de ese dominio para educar al resto de nosotros a través de una documentación increíble. Gracias por tu tiempo.

Todos 38 comentarios

Tienes razón, es a propósito. Y no creo que sea necesario declararlo explícitamente. Cualquiera que haya trabajado con IEEE 754 flotantes antes probablemente sepa que NaN es solo otro valor de punto flotante.

Relacionado: discusión en # 321

Mi punto al preguntar es que, cuando estoy haciendo tareas web comunes, una función llamada isNumber parece una buena forma de verificar si un valor que se encuentra responde a la definición de usuario común de "¿es esto un número?".

Además, me parece que el valor de NaN se introdujo en primer lugar para ayudar a identificar los valores que rompen los cálculos. En el espíritu de la especificación, parece que la respuesta correcta cuando hace la pregunta "¿es este un número?" es de hecho que es "No es un número". Si puede nombrar un ejemplo común en el que le gustaría que la respuesta fuera cierta para esta pregunta, me callaré ahora :)

Está buscando isFinite , que las personas que conocen JS ya deberían saber.

Ok, comencé con un problema simple. Tengo entradas que son cadenas, números y posiblemente NaN. Tengo una solución simple en JavaScript base que utiliza parseInt e isFinite para probar si el parse int tiene éxito. Simple pero no completamente limpio o autodescriptivo de mi objetivo. Entonces, decido usar mi increíble biblioteca goto para hacer este tipo de tareas. En la inspección inicial encuentro una función que dice que toma un valor y le dice si es un número, que según mi definición común es lo que creo que quiero.

Primero, con esto, ¿puede ver cómo agregar una línea a la documentación ayudaría a los usuarios de la biblioteca a obtener la función correcta para el trabajo la primera vez? En segundo lugar, pido nuevamente un ejemplo en el que esto sea significativo.

Parece que sabes todo lo que hay que saber sobre el idioma. Por lo tanto, en lugar de lanzar soluciones a ciegas en la oscuridad que mi hermano de octavo grado podría darme, tal vez sería bueno si pudieras usar algo de ese dominio para educar al resto de nosotros a través de una documentación increíble. Gracias por tu tiempo.

Me interesa lo que piensa @jashkenas . No quiero sonar insultante o condescendiente. Sé que a veces salgo así. Creo que es el hecho de que estoy tratando de dirigir la conversación en una dirección que muestre mejor el problema al dar estas respuestas simples y ver cómo fallan en resolver el problema en sus ojos.

Primero, con esto, ¿puede ver cómo agregar una línea a la documentación ayudaría a los usuarios de la biblioteca a obtener la función correcta para el trabajo la primera vez?

Posiblemente seguro.

En segundo lugar, pido nuevamente un ejemplo en el que esto sea significativo.

Prueba para [[Clase]] == "Número"? Eso debería ser obvio, coincide con los números. Tal vez no sean números naturales o enteros o números finitos o números reales o números racionales o cualquier subconjunto que esperabas, pero seguramente todos son números.

Dicho todo esto, hay ocasiones en las que agregar demasiadas pelusas en la documentación puede ser perjudicial. Pero no creo que este sea uno de esos momentos. Esto podría ser potencialmente útil para los desarrolladores que, en su estado actual, pueden tener una interpretación contradictoria del concepto abstracto de un número. Como en tu caso, donde ya habías establecido un modelo mental del conjunto para el que querías probar, y luego encontraste una función que _apareció_ para realizar esa prueba. +1 , aunque todavía estoy interesado en lo que piensa @jashkenas .

Sí, no es del todo obvio si _.isNumber devolverá true o false cuando se pase NaN . Ciertamente no lo recordaría sin probarlo. Se agregó una nota a la documentación en el compromiso anterior. Gracias.

Gracias chicos. Me alegra saber que es posible que otra persona no se encuentre con este mismo problema.

Este definitivamente parece un caso en el que la semántica debería prevalecer sobre los hechos técnicos. Sí, NaN puede ser un número de acuerdo con algunas especificaciones. Pero si hago algo la pregunta "¿Eres un número?" y dice "No soy un número", entonces debería creerlo. Not a Number - NaN - definitivamente no es un Número ... y isNumber debería devolver falso.

Poner el Gotcha en los documentos, en lugar de arreglar las cosas para que tengan sentido para los humanos, solo lleva a menos tiempo de codificación y más tiempo para examinar la documentación que te rasca la cabeza.

¿Se puede reconsiderar esto, por favor?

@contentfree : NaN es miembro de los flotadores. Cada número en JS es un flotador. Creo que eso hace que NaN sea ​​el número más posible. NaN no significa "No soy un número". Es la representación numérica de no números. Caso cerrado en mi libro. Use isFinite si desea probar números que no son NaN / Infinity / -Infinity .

No para vencer a un caballo muerto, sino ...

¿Por qué no simplemente hacer
obj instanceOf Number

Me parece que si hay una forma más legible de hacer la acción necesaria que ya existe en la especificación de javascript, lo haré de esa manera sin usar una biblioteca con una biblioteca desconocida (a menos que pierda el tiempo investigando).

Voto que si no vamos a arreglar esto para que sea semánticamente correcto, simplemente deberíamos eliminar los gastos generales inútiles y el dolor de cabeza de la biblioteca.

El operador instanceof solo funcionará para objetos, no para primitivas: new Number(5) instanceof Number == true; 5 instanceof Number == false . Tampoco funcionará en los marcos de los navegadores. [[Class]] comprobación Object::toString se acepta generalmente como el método de comprobación de tipos más fiable en JavaScript.

+1 para hacer que la biblioteca sea más útil y, al mismo tiempo, educar a los usuarios nuevos en JavaScript.

Sugerencia:
_.isNumber (objeto, [isFinite]);

Esto permitirá una simple adición de un verdadero a su implementación actual cuando sea atrapado por este problema mientras aprende que isFinite era probablemente lo que buscaba en primer lugar.

Mi 2c

@ nickl- Idea interesante, pero ... ¿por qué no usar simplemente isFinite en ese caso?

Alternativamente, dado que _.isNaN ya existe, _.isFinite podría agregarse potencialmente por simetría:

_.isFinite = function (value) {
  return value > -1 / 0 && value < 1 / 0;
};

@kitcambridge

El único problema que preveo con eso es que las cadenas pasarían como finitas "0x0", "0xF", "2", etc. Así que pase lo que pase, tendrá que combinarse con _.isNumber o equivalente:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && _.isNumber(obj);
};

Puede eliminar cinco caracteres usando val === + val para pruebas numéricas:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && obj === +obj;
};

@octatone Claro, creo que eso es justo ... técnicamente, esas cadenas _son_ finitas, ya que se pueden usar en comparaciones numéricas (el intérprete debe convertirlas en números), pero su propuesta es más consistente con las funciones de verificación de tipos de subrayado existentes.

¿Qué pasa con _, isValidNumber que está en línea con el _.isValidDate existente y no confunde el argumento isFinite?

@ nickl-

NaN y Infinity _son_ números válidos. Creo que nombrar esta función isValidNumber confundiría su propósito o ¿te refieres a cambiar el nombre de isNumber a isValidNumber?

NaN y Infinity _son_ números válidos. Creo que nombrar esta función isValidNumber confundiría su propósito.

Estoy de acuerdo.

No dije renombrar ...

También me sorprendió descubrir que _.isNumber(NaN) === true .

Creo que tiendo a estar de acuerdo con https://github.com/jashkenas/underscore/issues/406#issuecomment -4144992 de @ contentfree. En ingeniería de software incluso tenemos un principio para este caso específico: Principio del menor asombro . ;-)

Y después de ver a tanta gente confundida al respecto, creo que tendría sentido hacer que la función haga lo que los humanos que no son codificadores de js de núcleo duro esperan que haga cuando miran su nombre.

Solo mi pequeño 2 ¢… ™

_.isNaN también es confuso. En el documento:

Nota: esto no es lo mismo que la función nativa isNaN, que también devolverá verdadero para muchos otros valores no numéricos, como undefined.

en sentido normal undefined es de hecho No-A-Número.

Voto por la constructividad semántica, isNumber == Not a number lol. Creo, si necesita entrar en detalles sobre la verificación de valores de punto flotante, ¿entonces hacerlo de otra manera?

Voto por la constructividad semántica,

No, es un número de [[Class]] como -Infinity , Infinity , & Object(2) . Es una de esas cosas que los desarrolladores aprenden, como que las funciones también son objetos. Lo más probable es que desee realizar algún tipo de validación en su número que está fuera del alcance de este método. Por ejemplo, es mayor que -1 , menor que Math.pow(2,32) o un número entero. En los casos de -Infinity , Infinity o NaN utilizo _.isFinite como validador. De manera similar, _.isDate no valida si el objeto de fecha representa una fecha válida.

¿Qué pasa con la actualización de la documentación con un "ver también: isFinite" y "ver también:« ensayo que enumera métodos para determinar si algo es un valor numérico que realmente puede usar »"

@michaelficarra Sé que en la especificación NaN es un tipo numérico. Pero, ¿es eso lo que un programador está pensando cuando pregunta si es un número?

Lo que la mayoría de nosotros queremos saber, la mayor parte del tiempo es, ¿puedo usar esto para aritmética básica? Entonces tienes que ir a isNumber (x) && isFinite (x). Y eso está bien, supongo. Pero es un gran problema para un programador nuevo y no lee bien.

Desde un punto de vista estrictamente del idioma inglés, Not a Number (NaN) que devuelve verdadero de una prueba llamada isNumber no tiene ningún sentido. ¿No se llamaría mejor isNumeric o isNumericType?

No tengo ninguna duda de que esto agregará errores y desperdiciará muchas horas, al menos la primera vez que la gente se enfrenta a esto.

Entonces tienes que ir a isNumber (x) && isFinite (x).

Recientemente alineé una implementación de _.isFinite para seguir a ES6 Number.isFinite .
Asegura que el valor sea un número primitivo, finito y debería manejar este caso común.

No es necesario cambiar el comportamiento de _.isNumber que se alinea con los otros métodos de verificación [[Class]] .

Desde un punto de vista estrictamente del idioma inglés, Not a Number (NaN) que devuelve verdadero de una prueba llamada isNumber no tiene ningún sentido. ¿No se llamaría mejor isNumeric o isNumericType?

Vea mi comentario sobre validación, _.isNumber y _.isDate .

@jdalton Tiene razón en que el comportamiento es constante con _.isDate. Y ustedes hacen un gran trabajo con el subrayado, así que creo que la decisión realmente recae en ustedes.

Pero parece tan contrario a la intuición que algo llamado no número sea un número.

@Walms 100% de acuerdo. Creo que en este caso, el programador y la intuición deberían anular la "corrección" absoluta de la declaración, dado que NaN es _técnicamente_ numérico.

Pero parece tan contrario a la intuición que algo llamado no número sea un número.

En ese contexto, ¿es más contrario a la intuición que _.isNumber devolver true para objetos numéricos, Object(2) que son del tipo object ?

Estos son métodos [[Class]] . Quizás eso deba quedar más claro en la documentación.

Ya he propuesto una alternativa viable que es simplemente hacer que _.isFinite siga a ES6 Number.isFinite . Si quieres un is-number básico, usa typeof x == 'number' . Si desea alguna validación de que el valor no es NaN , Infinity o -Infinity , todos [[Class]] de Number , entonces _.isFinite es el camino a seguir.

Busque subrayado-contrib para métodos numéricos más detallados. Tiene _.isNumeric , _.isInteger , _.isZero , _.isEven , _.isOdd , _.isFloat , _.isNegative , & _.isPositive .

@jdalton Creo que sus dos últimos comentarios realmente han ayudado a aclarar por qué creo que esto es confuso.

Inicialmente no vi que a _.isNumber se le dio un contexto por el hecho de que tenía el prefijo is, lo que significa que era una verificación de tipo. Con este contexto, está totalmente en lo cierto, no tiene sentido que _.isNumber devuelva falso para NaN.

Pero luego el subrayado-contrib e isFinite parecen romper este contexto. Como comienzan con es, sin embargo, miran el valor en lugar del tipo. Y aquí es donde creo que la confusión es que no hay una forma clara de determinar el contexto a partir del nombre de la función.

Dicho todo esto, no veo ninguna forma de solucionarlo.

Creo que el problema aquí es que hay algunas cosas sobre Numbers que no son intuitivas. Sin embargo, se comportan de manera consistente. Sería posible hacer que _.isNumber más intuitivo en este caso haciéndolo menos consistente, lo que finalmente lo haría menos intuitivo en otros casos.

A continuación se muestran algunos ejemplos concretos:

# If you add two Numbers together you always get another Number back
# This function should always return true no matter what you pass it
closedUnderAddition = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a + b)

closedUnderAddition(1,1) == true
closedUnderAddition(Number.MAX_INT,2**970) == true # false if isNumber checks finiteness

# If you divide two Numbers you always get another Number back
closedUnderDivision = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a / b)

closedUnderDivision(1, 2) == true
closedUnderDivision(1, 0) == true # false if isNumber checks finiteness
closedUnderDivision(0, 0) == true # false if isNumber checks finiteness or NaN-ness

# Anything you cast to a Number is a Number
castsToNumber = (x) -> isNumber(Number(x))

castsToNumber(1) == true
castsToNumber(Infinity) == true # false if isNumber checks finiteness
castsToNumber("bees") == true # false if isNumber checks finiteness or NaN-ness
castsToNumber("3") == true

Como dijo michaelficarra:

NaN no significa "no soy un número". Es la representación numérica de no números.

O, para decirlo de otra manera, hay "números" y Números. Puede que NaN no sea un "número", pero es absolutamente un Número junto con Infinity, -Infinity y cosas ridículas como -0. Sin embargo, a pesar de lo salvaje y lanoso que es, la definición de Número está bien especificada y se comporta de manera coherente.

"número" por otro lado es un concepto mal definido que, dependiendo de con quién estés hablando, podría incluir alguno o ninguno de los anteriores , y tal vez cadenas numéricas, bools, medianoche y todo tipo de cosas. Está bien, pero nunca habrá una solución que se adhiera a la definición de todos.

Creo que por esa razón esto es esencialmente un problema de documentación.

¿También "número" ha dejado de parecerse a una palabra para alguien más?

Buenos puntos, @sgentle. Voto por la coherencia sobre la intuición, ya que la primera es una medida objetiva, mientras que no todos estamos de acuerdo en lo que es intuitivo (como demuestra este hilo).

Simplemente no trate a NaN como "No es un número". NaN es un valor especial.

Simplemente no trate a NaN como "No es un número". NaN es un valor especial.

Sí, pero se llama "No es un número".
Para mi eso es asi
var _false = "verdadero";
Sí, puedes aprender que _false es cierto, pero es confuso sin una buena razón.

@Walms Es un mal nombre, pero eso no es culpa de JS. Podemos culpar al tipo que lo nombró como "NaN". http://en.wikipedia.org/wiki/NaN

Aargh, desearía que _.isNumber(NaN) devolviera false ... un serio rascado de cabeza por aquí hasta que me di cuenta de que esta era la razón.

if (isNaN(Number(value))) {
  alert('Number required.');
}

@pspi De acuerdo. Hacer que _.isNumber() comporte correctamente en el sentido _ semántico_ significa que nunca lo usaré. ._isFinite() parece ser la función que funciona de la manera que esperaba.

Y, por supuesto, un día después de publicar esto, descubro que _.isFinite('1') es true , aunque el argumento no es un número.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

jdalton picture jdalton  ·  4Comentarios

marcalj picture marcalj  ·  5Comentarios

sky0014 picture sky0014  ·  8Comentarios

jezen picture jezen  ·  8Comentarios

danilopolani picture danilopolani  ·  5Comentarios