Back

La Extraña Vida de NaN en JavaScript

La Extraña Vida de NaN en JavaScript

Ya lo has visto antes: un cálculo devuelve NaN, y de repente todo tu pipeline de datos produce basura. O peor aún, una comparación que debería detectar el problema falla silenciosamente porque NaN === NaN devuelve false. Comprender las peculiaridades de NaN en JavaScript no es opcional—es esencial para escribir código numérico confiable.

Este artículo explica por qué NaN se comporta de la manera en que lo hace, dónde aparece comúnmente y cómo detectarlo correctamente usando métodos modernos.

Puntos Clave

  • NaN es un tipo de número válido según IEEE 754, que representa resultados matemáticos indefinidos o no representables.
  • NaN nunca es igual a sí mismo—usa Number.isNaN() u Object.is() para una detección confiable.
  • La función global isNaN() coerciona los argumentos primero, causando falsos positivos; evítala.
  • La serialización JSON convierte silenciosamente NaN a null, mientras que structuredClone() lo preserva.
  • Valida las entradas numéricas en los límites para evitar que NaN se propague a través de los cálculos.

Por Qué Existe NaN y Por Qué typeof Devuelve “number”

NaN significa “Not a Number” (No es un Número), sin embargo typeof NaN devuelve "number". Esto no es un error de JavaScript—es por diseño.

El estándar de punto flotante IEEE 754, que JavaScript sigue, define NaN como un valor especial dentro del tipo number. Representa el resultado de operaciones matemáticas indefinidas o no representables. Piensa en él como un marcador de posición que dice “este cálculo no produjo un resultado numérico válido.”

console.log(typeof NaN) // "number"

NaN existe porque las operaciones numéricas necesitan una forma de señalar un fallo sin lanzar excepciones. La división por cero con enteros causaría un crash en muchos lenguajes, pero 0 / 0 devuelve NaN y permite que tu programa continúe.

Fuentes Comunes de NaN en JavaScript

NaN aparece con más frecuencia de lo que podrías esperar:

// Análisis fallido
Number("hello")        // NaN
parseInt("abc")        // NaN

// Operaciones matemáticas inválidas
0 / 0                  // NaN
Math.sqrt(-1)          // NaN
Infinity - Infinity    // NaN

// Operaciones con undefined
undefined * 5          // NaN

Las entradas de formularios son un culpable frecuente. Cuando parseFloat(userInput) encuentra texto no numérico, NaN entra silenciosamente en tus cálculos y se propaga a través de cada operación subsecuente.

Reglas de Comparación de NaN: Por Qué NaN !== NaN

Aquí está el comportamiento que confunde a la mayoría de los desarrolladores:

NaN === NaN  // false
NaN !== NaN  // true

IEEE 754 lo exige así. El razonamiento: si dos operaciones fallan, no puedes asumir que sus “fallos” son equivalentes. Math.sqrt(-1) y 0 / 0 ambos producen NaN, pero representan diferentes resultados indefinidos.

Esto significa que no puedes usar operadores de igualdad para detectar NaN.

Number.isNaN vs isNaN: La Diferencia Crítica

JavaScript proporciona dos funciones para la detección de NaN, pero solo una funciona de manera confiable.

La función global isNaN() coerciona su argumento a un número primero:

isNaN("hello")     // true (coerciona a NaN, luego verifica)
isNaN(undefined)   // true
isNaN({})          // true

Esto produce falsos positivos. La cadena "hello" no es NaN—es una cadena que se convierte en NaN cuando es coercionada.

Number.isNaN() verifica sin coerción:

Number.isNaN("hello")     // false
Number.isNaN(undefined)   // false
Number.isNaN(NaN)         // true

Siempre usa Number.isNaN() para una detección precisa. Alternativamente, Object.is(value, NaN) también funciona correctamente:

Object.is(NaN, NaN)  // true

Comportamiento de NaN en JSON: Pérdida Silenciosa de Datos

Cuando serializas datos que contienen NaN, JSON.stringify() lo reemplaza con null:

JSON.stringify({ value: NaN })  // '{"value":null}'
JSON.stringify([1, NaN, 3])     // '[1,null,3]'

Esta es una fuente común de errores al enviar datos numéricos a APIs o almacenarlos en bases de datos. Tus valores NaN desaparecen silenciosamente, y recibes null de vuelta—lo cual puede causar diferentes errores más adelante.

Valida los datos numéricos antes de la serialización si la preservación de NaN es importante.

structuredClone y NaN: Preservación en la Clonación Moderna

A diferencia de JSON, structuredClone() preserva los valores NaN:

const original = { score: NaN }
const cloned = structuredClone(original)

Number.isNaN(cloned.score)  // true

Esto hace que structuredClone() sea seguro para NaN al copiar profundamente objetos que pueden contener resultados numéricos inválidos. Si estás clonando estructuras de datos con posibles valores NaN, prefiere structuredClone() sobre el round-tripping de JSON.

Propagación de NaN: El Efecto Viral

Una vez que NaN entra en un cálculo, infecta cada resultado:

const result = 5 + NaN      // NaN
const final = result * 100  // NaN

Esta propagación es intencional—te previene de usar accidentalmente datos corruptos. Pero significa que un solo NaN temprano en un pipeline puede invalidar un conjunto de datos completo.

Valida las entradas en los límites: al analizar entrada del usuario, recibir respuestas de API o leer de fuentes externas.

Conclusión

El comportamiento extraño de NaN sigue reglas lógicas una vez que comprendes el diseño de IEEE 754. Usa Number.isNaN() u Object.is() para la detección—nunca operadores de igualdad o la función global isNaN(). Recuerda que la serialización JSON convierte NaN a null, mientras que structuredClone() lo preserva. Valida los datos numéricos en los puntos de entrada para detectar NaN antes de que se propague a través de tus cálculos.

Preguntas Frecuentes

NaN está definido como un valor numérico especial por el estándar de punto flotante IEEE 754, que JavaScript implementa. Representa el resultado de operaciones matemáticas indefinidas mientras permanece dentro del sistema de tipos numéricos. Este diseño permite que los cálculos numéricos continúen sin lanzar excepciones cuando las operaciones fallan.

No. NaN es el único valor en JavaScript que no es igual a sí mismo. Tanto NaN === NaN como NaN == NaN devuelven false. Usa Number.isNaN() u Object.is(value, NaN) en su lugar para una detección confiable.

La función global isNaN() coerciona su argumento a un número antes de verificar, causando falsos positivos para valores no numéricos como cadenas o undefined. Number.isNaN() no realiza coerción y devuelve true solo si el valor es exactamente NaN. Siempre prefiere Number.isNaN() para resultados precisos.

Valida todas las entradas numéricas en los puntos de entrada como campos de formulario, respuestas de API y fuentes de datos externas. Verifica los valores con Number.isNaN() inmediatamente después de analizar o recibir datos. Esto detiene que NaN se propague a través de operaciones subsecuentes e invalide tus resultados.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay