Por que isNaN (null) == false em JS?

129

Esse código em JS me fornece um pop-up dizendo "eu acho que nulo é um número", o que acho um pouco perturbador. o que estou perdendo?

if (isNaN(null)) {
  alert("null is not a number");
} else {
  alert("i think null is a number");
}

Estou usando o Firefox 3. Isso é um bug do navegador?

Outros testes:

console.log(null == NaN);   // false
console.log(isNaN("text")); // true
console.log(NaN == "text"); // false

Então, o problema parece não ser uma comparação exata com o NaN?

Edit: Agora que a pergunta foi respondida, limpei minha postagem para ter uma versão melhor para o arquivo. No entanto, isso torna alguns comentários e até algumas respostas um pouco incompreensíveis. Não culpe seus autores. Entre as coisas que mudei estava:

  • Removi uma nota dizendo que eu tinha estragado a manchete, revertendo seu significado
  • As respostas anteriores mostraram que eu não afirmei com clareza o motivo pelo qual achei o comportamento estranho, então adicionei os exemplos que verificam uma string e fazem uma comparação manual.
Hanno Fietz
fonte
3
você não quer dizer "Por que isNaN (nulo) == falso"?
Matt Rogish 22/09/08
Com base no seu código, isnan (null) está retornando false (null não é "não é um número") se ele disser "Acho que nulo é um número".
devinmoore

Respostas:

114

Eu acredito que o código está tentando perguntar "é xnumérico?" com o caso específico aqui de x = null. A função isNaN()pode ser usada para responder a essa pergunta, mas semanticamente está se referindo especificamente ao valor NaN. Da Wikipedia para NaN:

NaN ( N ot uma N úmero) é um valor do tipo de dados numéricos representando um valor indefinido ou não representável, especialmente nos cálculos de ponto flutuante.

Na maioria dos casos, achamos que a resposta para "é numérico nulo?" deve ser não. No entanto, isNaN(null) == falseé semanticamente correto, porque nullnão é NaN.

Aqui está a explicação algorítmica:

A função isNaN(x)tenta converter o parâmetro passado em um número 1 (equivalente a Number(x)) e depois testa se o valor é NaN. Se o parâmetro não puder ser convertido em um número, Number(x)retornará NaN2 . Portanto, se a conversão do parâmetro xem um número resultar NaN, ele retornará verdadeiro; caso contrário, ele retornará false.

Portanto, no caso específico x = null, nullé convertido para o número 0 (tente avaliar Number(null)e veja se ele retorna 0) e isNaN(0)retorna false. Uma cadeia de caracteres com apenas dígitos pode ser convertida em um número e isNaN também retorna false. Uma string (por exemplo 'abcd') que não pode ser convertida em um número fará com isNaN('abcd')que retorne true, especificamente porque Number('abcd')retorna NaN.

Além desses casos de arestas aparentes, existem as razões numéricas padrão para retornar NaN como 0/0.

Quanto aos testes aparentemente inconsistentes de igualdade mostrados na pergunta, o comportamento de NaNé especificado de modo que qualquer comparação x == NaNseja falsa, independentemente do outro operando, incluindo o NaNpróprio 1 .

Glenn Moss
fonte
8
BTW NaN !== NaN,. Então, acho que não é totalmente correto dizer Number('abcd') == NaNporque Number('abcd')é, NaNmas não é igual a NaN. Eu adoro JavaScript.
Nilfalse
Sim. Eu quis transmitir que Number('abcd')é NaN, mas eu entender que ele testa verdadeiro para a igualdade, o que não é o caso. Eu vou editá-lo.
Glenn Moss
Eu me pergunto por que são isNaNe são Numberprojetados para se comportar dessa maneira?
timidboy
1
A conversão de nullpara 0somente (pelo menos neste contexto) ocorre dentro da isNaN()função, que coagula seu argumento.
Glenn Moss
3
Número (nulo) == 0, mas parseInt (nulo) == NaN love JS
JoshBerke
31

Acabei de me deparar com esse problema.

Para mim, a melhor maneira de usar isNaN é assim

isNaN(parseInt(myInt))

tomando o exemplo do phyzome de cima,

var x = [undefined, NaN,     'blah', 0/0,  null, 0,     '0',   1,     1/0, -1/0,  Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
        [true,      true,    true,   true, true, false, false, false, true, true, false]

(Alinhei o resultado de acordo com a entrada, espero que facilite a leitura.)

Isso me parece melhor.

guy mograbi
fonte
1
Não funcionará se myInt= "123d". parseIntconverte "123d" para 123, que falha no isNaNteste.
Divd premdeep
de fato, se você também quiser entender esse cenário, sugiro combinar minha resposta e a de Glenn. que ficará assim isNaN(parseInt(str,10)) || isNaN(Number()). btw - para mim, já que tenho que executar parseIntpara usar o valor numérico da string, permitindo que "123d" seja considerado como número válido. No entanto, vejo a necessidade de detectar esse cenário também.
guy mograbi
8

(Meu outro comentário adota uma abordagem prática. Aqui está o lado teórico.)

Olhei para cima do padrão ECMA 262 , que é o que implementa Javascript. Sua especificação para isNan:

Aplica ToNumber ao seu argumento, depois retorna true se o resultado for NaN e, caso contrário, retorna false.

A Seção 9.3 especifica o comportamento de ToNumber(que não é uma função que pode ser chamada, mas um componente do sistema de conversão de tipos). Para resumir a tabela, certos tipos de entrada podem produzir um NaN. Estes são tipo undefined, tipo number(mas apenas o valor NaN), qualquer objeto cuja representação primitiva seja NaNe qualquer um stringque não possa ser analisado. Isso deixa undefined, NaN, new Number(NaN), e a maioria das cordas.

Qualquer entrada que produza NaNcomo saída quando passada para ToNumberproduz um a truequando alimentada isNaN. Como nullpode ser convertido com sucesso em um número, ele não produz true.

E é por isso.

trate bem seus mods
fonte
7

Isso é realmente perturbador. Aqui está uma matriz de valores que eu testei:

var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]

Ele avalia (no console do Firebug):

,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5

Quando ligo x.map(isNaN)(chamar isNaN em cada valor), recebo:

true,true,true,true,false,false,false,false,false,false,false

Em conclusão, isNaNparece bastante inútil! ( Edit : exceto que isNaN é definido apenas sobre Number, caso em que funciona muito bem - apenas com um nome enganador.)

Aliás, aqui estão os tipos desses valores:

x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number
trate bem seus mods
fonte
O que você acha que NaN deveria significar? O que você acha que é tão enganador sobre o NaN ou para testá-lo? Por que você está perturbado?
Bekim Bacaj
Bem, isso foi há 8 anos, mas parece que fiquei perturbado por 1) ter resultados inconsistentes para valores que não são do tipo Número e 2) sempre retornar verdadeiro para algo que não é do tipo Número. Porque uma string não é, de fato, um NaN. (Veja também a minha outra resposta, o que explica por que isso acontece.)
tratar os seus mods bem
5

Nulo não é NaN, assim como uma string não é NaN. isNaN () apenas teste se você realmente possui o objeto NaN.

aparelho
fonte
Mas, pelo menos, uma string é convertida em um objeto NaN, pois isNaN ("text") retorna true.
Hanno Fietz
1

Não sei exatamente quando se trata de JS, mas já vi coisas semelhantes em outras linguagens e geralmente é porque a função está apenas verificando se nulo é exatamente igual a NaN (ou seja, nulo === NaN seria falso). Em outras palavras, não é que ele ache que nulo seja de fato um número, mas sim que nulo não é NaN. Provavelmente, porque ambos são representados de maneira diferente no JS, para que não sejam exatamente iguais, da mesma forma que 9! == '9'.

Jason Tennier
fonte
1

No ES5, ele define como isNaN (number)retorna true se o argumento for coercitivo para NaN e, caso contrário, retornará false.

E consulte a tabela de conversão ToNumber da operação abstrata . Por isso motor internamente js avaliar ToNumber(Null)é +0, então, eventualmente, isNaN(null)éfalse

rab
fonte
0

Nota:

"1" == 1 // true
"1" === 1 // false

O operador == faz conversão de tipo, enquanto === não.

Site de Douglas Crockford , um site do Yahoo! Evangelista JavaScript, é um ótimo recurso para coisas assim.

cllpse
fonte