NaN e typeof.

Uma análise da propriedade NaN e por que ela é considerada um tipo de número.

typeof NaN
//'number'

Primeiramente, NaN é uma propriedado do objeto global, e não uma palavra-chave (diferente de true, false, null, entre outros). O valor de NaN é o mesmo que o valor de Number.NaN:

NaN; // NaN
Number.NaN; // NaN

Existem várias formas em que o NaN pode acontecer:

  • Divisão de zero por zero;
  • Dividindo um infinito por um infinito;
  • Multiplicação de um infinito por um zero;
  • Qualquer operação na qual NaN é um operando;
  • Convertendo uma String não numérica ou indefinida em um número.

Por que o tipo NaN retorna “number”?

typeof NaN; // "number"

O padrão ECMAScript indica que os números devem ser dados de ponto flutuante IEEE-754. Isso inclui Infinity, -Infinity e também, NaN. Por definição, NaN é o valor de retorno de operações que possuem um resultado numérico indefinido. Daí porque, em JavaScript, além de fazer parte do objeto global, também faz parte do objeto Number: Number.NaN.

Ainda é um tipo de dados numérico, mas é indefinido como um número real. O NaN também representa qualquer número fora do domínio de definição do ECMAScript.

A aritmética computacional é limitada:

Considere a seguinte operação:

(3.2317006071311 * 10e616) / (3.2317006071311 * 10e616); // NaN
Como a Wikipedia declara:

“A aritmética computacional não pode operar diretamente em números reais, mas apenas em um subconjunto finito de números racionais, limitado pelo número de bits usados para armazená-los.”

Na aritmética ordinária, 3,2317006071311 * 10616 é um número real finito, mas, pelos padrões ECMAScript, é simplesmente muito grande (ou seja, consideravelmente maior que Number.MAX_VALUE) e, portanto, é representado como Infinity.

A tentativa de dividir Infinity por Infinity produzirá NaN. Na aritmética comum, ambos são operandos finitos, a operação claramente é igual a 1. Neste caso, o NaN está no lugar de um numero real que não pôde ser computado devido ao tamanho dos operandos. Pareceria contra-intuitivo se os tipos NaN retornassem algo diferente de “número”. Afinal, no exemplo dado, NaN simplesmente representa um valor que não pôde ser determinado pela aritmética computacional.

NaN é desordenado:

De acordo com o padrão de ponto flutuante IEEE 754, a comparação com NaN sempre retorna um resultado não ordenado. Ou seja, NaN não é igual a, maior que ou menor que qualquer coisa, incluindo ele mesmo:

NaN < 1;    // false
NaN > 1;    // false
NaN == NaN; // false
// Mas ainda podemos verificar por NaN:
isNaN(NaN); // true

É por isso que você não pode determinar se um dado valor é NaN comparando-o com NaN e, em vez disso, deve usar a função isNaN().

Não surpreende, portanto, que a implementação nativa da função isNaN() possa ser simplesmente substituída por:

// Implementacao Nativa
function isNaN(x) {
  x = Number(x);
  // se x é NaN, NaN! = NaN é verdadeiro, senão é falso
  return x != x;
}

A implementação nativa de isNaN() retorna true mesmo se o valor for indefinido ou se o valor não puder ser coagido em um tipo de dados de número primitivo. Existem algumas bibliotecas que apresentam as suas próprias implementações. Por exemplo, a do Underscore é a seguinte:

_.isNaN = function(obj) {
  // `NaN` é o único valor para o qual` === `não é reflexivo.
  return obj !== obj;
};

Mas, seu comportamento não é o mesmo que a função isNaN() nativa:

let x;            // undefined
isNaN(x);         // true
isNaN(undefined); // true
isNaN("a");       // true
Comparado ao Underscore:
let x;              // undefined
_.isNaN(x);         // false
_.isNaN(undefined); // false
_.isNaN("a");       // false

Booleanos não são NaNs:

Considerando o código a seguir:

isNaN(true);  // false
isNaN(false); // false

Isso ocorre porque os valores booleanos são considerados e implementados como valores numéricos com um único dígito binário (ou seja, bit), portanto, eles são coagidos em suas respectivas representações de bits:

Number(true);  // 1
Number(false); // 0

Gostou deste artigo? Comente abaixo!

Deixe um comentário