Back

A Vida Estranha de NaN em JavaScript

A Vida Estranha de NaN em JavaScript

Você já viu isso antes: um cálculo retorna NaN, e de repente todo o seu pipeline de dados produz lixo. Ou pior, uma comparação que deveria detectar o problema falha silenciosamente porque NaN === NaN retorna false. Compreender as peculiaridades do NaN em JavaScript não é opcional—é essencial para escrever código numérico confiável.

Este artigo explica por que NaN se comporta da maneira que se comporta, onde ele aparece comumente e como detectá-lo corretamente usando métodos modernos.

Pontos-Chave

  • NaN é um tipo de número válido segundo o IEEE 754, representando resultados matemáticos indefinidos ou não representáveis.
  • NaN nunca é igual a si mesmo—use Number.isNaN() ou Object.is() para detecção confiável.
  • O isNaN() global força coerção de argumentos primeiro, causando falsos positivos; evite-o.
  • A serialização JSON converte silenciosamente NaN para null, enquanto structuredClone() o preserva.
  • Valide entradas numéricas nos limites para evitar que NaN se propague através dos cálculos.

Por Que NaN Existe e Por Que typeof Retorna “number”

NaN significa “Not a Number” (Não é um Número), mas typeof NaN retorna "number". Isso não é um bug do JavaScript—é intencional.

O padrão de ponto flutuante IEEE 754, que o JavaScript segue, define NaN como um valor especial dentro do tipo number. Ele representa o resultado de operações matemáticas indefinidas ou não representáveis. Pense nele como um marcador que diz “esta computação não produziu um resultado numérico válido.”

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

NaN existe porque operações numéricas precisam de uma forma de sinalizar falha sem lançar exceções. Divisão por zero com inteiros causaria crash em muitas linguagens, mas 0 / 0 retorna NaN e permite que seu programa continue.

Fontes Comuns de NaN em JavaScript

NaN aparece com mais frequência do que você pode imaginar:

// Falha no parsing
Number("hello")        // NaN
parseInt("abc")        // NaN

// Operações matemáticas inválidas
0 / 0                  // NaN
Math.sqrt(-1)          // NaN
Infinity - Infinity    // NaN

// Operações com undefined
undefined * 5          // NaN

Entradas de formulários são uma causa frequente. Quando parseFloat(userInput) encontra texto não numérico, NaN entra silenciosamente em seus cálculos e se propaga através de cada operação subsequente.

Regras de Comparação de NaN: Por Que NaN !== NaN

Aqui está o comportamento que confunde a maioria dos desenvolvedores:

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

O IEEE 754 determina isso. O raciocínio: se duas operações falham, você não pode assumir que suas “falhas” são equivalentes. Math.sqrt(-1) e 0 / 0 ambos produzem NaN, mas representam resultados indefinidos diferentes.

Isso significa que você não pode usar operadores de igualdade para detectar NaN.

Number.isNaN vs isNaN: A Diferença Crítica

JavaScript fornece duas funções para detecção de NaN, mas apenas uma funciona de forma confiável.

O isNaN() global força coerção de seu argumento para número primeiro:

isNaN("hello")     // true (força coerção para NaN, depois verifica)
isNaN(undefined)   // true
isNaN({})          // true

Isso produz falsos positivos. A string "hello" não é NaN—é uma string que se torna NaN quando sofre coerção.

Number.isNaN() verifica sem coerção:

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

Sempre use Number.isNaN() para detecção precisa. Alternativamente, Object.is(value, NaN) também funciona corretamente:

Object.is(NaN, NaN)  // true

Comportamento de NaN em JSON: Perda Silenciosa de Dados

Quando você serializa dados contendo NaN, JSON.stringify() o substitui por null:

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

Esta é uma fonte comum de bugs ao enviar dados numéricos para APIs ou armazená-los em bancos de dados. Seus valores NaN desaparecem silenciosamente, e você recebe null de volta—o que pode causar erros diferentes posteriormente.

Valide dados numéricos antes da serialização se a preservação de NaN for importante.

structuredClone e NaN: Preservação na Clonagem Moderna

Diferentemente do JSON, structuredClone() preserva valores NaN:

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

Number.isNaN(cloned.score)  // true

Isso torna structuredClone() seguro para NaN ao copiar profundamente objetos que podem conter resultados numéricos inválidos. Se você está clonando estruturas de dados com potenciais valores NaN, prefira structuredClone() ao invés de conversão via JSON.

Propagação de NaN: O Efeito Viral

Uma vez que NaN entra em um cálculo, ele infecta cada resultado:

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

Esta propagação é intencional—ela evita que você use acidentalmente dados corrompidos. Mas significa que um único NaN no início de um pipeline pode invalidar um conjunto de dados inteiro.

Valide entradas nos limites: ao fazer parsing de entrada do usuário, receber respostas de API ou ler de fontes externas.

Conclusão

O comportamento estranho de NaN segue regras lógicas uma vez que você compreende o design do IEEE 754. Use Number.isNaN() ou Object.is() para detecção—nunca operadores de igualdade ou o isNaN() global. Lembre-se que a serialização JSON converte NaN para null, enquanto structuredClone() o preserva. Valide dados numéricos nos pontos de entrada para capturar NaN antes que ele se propague através de seus cálculos.

Perguntas Frequentes

NaN é definido como um valor numérico especial pelo padrão de ponto flutuante IEEE 754, que o JavaScript implementa. Ele representa o resultado de operações matemáticas indefinidas enquanto permanece dentro do sistema de tipos number. Este design permite que computações numéricas continuem sem lançar exceções quando operações falham.

Não. NaN é o único valor em JavaScript que não é igual a si mesmo. Tanto NaN === NaN quanto NaN == NaN retornam false. Use Number.isNaN() ou Object.is(value, NaN) para detecção confiável.

O isNaN() global força coerção de seu argumento para número antes de verificar, causando falsos positivos para valores não numéricos como strings ou undefined. Number.isNaN() não realiza coerção e retorna true apenas se o valor for exatamente NaN. Sempre prefira Number.isNaN() para resultados precisos.

Valide todas as entradas numéricas nos pontos de entrada, como campos de formulário, respostas de API e fontes de dados externas. Verifique valores com Number.isNaN() imediatamente após fazer parsing ou receber dados. Isso impede que NaN se propague através de operações subsequentes e invalide seus 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