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!