Qual é a diferença entre (NaN! = NaN) e (NaN! == NaN)?

148

Antes de tudo, quero mencionar que sei como isNaN()e Number.isNaN()trabalho. Estou lendo O guia definitivo de David Flanagan e ele fornece um exemplo de como verificar se o valor é NaN:

x !== x

Isso resultará em truese e somente se xfor NaN.

Mas agora eu tenho uma pergunta: por que ele usa uma comparação estrita? Porque parece que

x != x

se comporta da mesma maneira. É seguro usar as duas versões ou faltam alguns valores em JavaScript que retornarão truepara x !== xe falsepara x != x?

Giorgi Nakeuri
fonte
10
Pode ser que Flanagan apenas prefira !==cheques sobre !=cheques. Tanto quanto sei, não há outro valor em que x != x. Mas existem dois grupos distintos de desenvolvedores de JavaScript: aqueles que preferem !=e aqueles que preferem !==, seja por velocidade, clareza, expressividade etc.
Steve Klösters -
30
Por que usar uma comparação flexível quando a comparação estrita se comporta da mesma maneira?
Ry-
3
@ Raulucco: NaNnão é um tipo único, é um número. É um valor único que não é igual a si mesmo.
TJ Crowder
8
O título parece ser pessoas enganadoras. Eu sugiro alterá-lo para algo como "x! = X é sempre diferente de x! == x?"
TJ Crowder
6
@femmestem: Giorgi disse que "neste caso" é uma questão de estilo. E ele está correto nisso. Não é estilo quando os tipos dos operandos são diferentes, mas é estilo quando são os mesmos. Separadamente: Flanagan está fazendo essas comparações ===com NaN para enfatizar que NaN não é igual a si mesmo. Ele não está "errado", está fazendo isso como um exercício de ensino, demonstrando que não funciona.
TJ Crowder

Respostas:

128

Primeiro, deixe-me salientar que NaNé um valor muito especial: por definição, não é igual a si mesmo. Isso vem do padrão IEEE-754 que os números JavaScript utilizam. O valor "não é um número" nunca é igual a si mesmo, mesmo quando os bits são uma correspondência exata. (Que eles não estão necessariamente no IEEE-754, ele permite vários valores diferentes de "não é um número".) É por isso que isso acontece; todos os outros valores em JavaScript são iguais a eles mesmos, NaNé apenas especial.

... estou perdendo algum valor no JavaScript que retornará verdadeiro para x! == xe falso para x! = x?

Não, você não é. A única diferença entre !==e !=é que este último fará a coerção de tipos, se necessário, para obter os tipos dos operandos iguais. Em x != x, os tipos dos operandos são os mesmos e, portanto, são exatamente os mesmos que x !== x.

Isso fica claro desde o início da definição da Operação de Igualdade Abstrata :

  1. ReturnIfAbrupt (x).
  2. ReturnIfAbrupt (y).
  3. Se o Tipo (x) for igual ao Tipo (y), então

    Retorne o resultado da realização de Comparação Rigorosa de Igualdade x === y.

  4. ...

Os dois primeiros passos são o encanamento básico. Portanto, com efeito, o primeiro passo ==é verificar se os tipos são iguais e, se for o caso, fazê-lo ===.!=e !==são apenas versões negadas disso.

Portanto, se Flanagan estiver correto, que somente NaNdará verdade para x !== x, podemos ter certeza de que também é verdade que somente NaNdará para x != x.

Muitos programadores de JavaScript usam o padrão ===e !==evitam algumas armadilhas em torno da coerção de tipo que os operadores soltos fazem, mas não há nada para ler sobre o uso de Flanagan do operador estrito versus frouxo neste caso.

TJ Crowder
fonte
Reli a 4.9.1 - Equality and Inequality Operatorsseção e essa parece ser a resposta. O ponto-chave para a ===comparação é: If the two values have the same type, test them for strict equality as described above. If they are strictly equal, they are equal. If they are not strictly equal, they are not equal.
Giorgi Nakeuri
@GiorgiNakeuri: Não sei ao que 4.9.1 você está se referindo, talvez o livro de Flanagan? Mas isso é basicamente dizer o que a citação da especificação acima está dizendo, sim.
TJ Crowder
2
Estou aceitando isso porque isso responde à minha pergunta de maneira formalizada e precisa. Obrigado pelas explicações!
Giorgi Nakeuri
1
@ Moshe: O que você quer dizer com "ligações ao vivo"? (O termo não aparece na especificação.) Você quer dizer algo como o exemplo do GOTO 0, onde ana verdade é uma função e não retorna o mesmo valor duas vezes? Não é a mesma coisa que um valor para o qual !==seria verdade, e é sobre isso que o OP perguntou. É apenas uma função que retorna valores diferentes. foo() !== foo()também não é necessariamente verdade, pois foopode retornar valores diferentes em cada chamada.
TJ Crowder
1
@ Moshe Bem, essa é uma maneira super desagradável de mexer com propriedades e getters. Mas parece ser praticamente o mesmo que o exemplo do GOTO 0, apenas com uma camada extra de indireção.
JAB
37

Para fins de NaN, !=e!== fazer a mesma coisa.

No entanto, muitos programadores evitam ==ou !=em JavaScript. Por exemplo, Douglas Crockford os considera entre as " partes ruins " da linguagem JavaScript porque se comportam de maneiras inesperadas e confusas:

O JavaScript tem dois conjuntos de operadores de igualdade: ===e !==, e seus gêmeos maus ==e!= . Os bons funcionam da maneira que você esperaria.

... Meu conselho é nunca usar os gêmeos do mal. Em vez disso, sempre use ===e !==.

jkdev
fonte
2
A pergunta não é sobre NaN (apesar do título). A pergunta é "estou perdendo algum valor no JavaScript que retornará verdadeiro para x! == xe falso para x! = X?"
TJ Crowder
@TJCrowder Duas perguntas, realmente. A primeira pergunta é "É seguro usar as duas versões" e a resposta é que ambas as versões são equivalentes. Eu gosto da sua resposta "secreta", que explica tudo em detalhes.
Jkdev 14/12/15
22

Apenas por diversão, deixe-me mostrar um exemplo artificial onde xnão existe, NaNmas os operadores se comportam de maneira diferente de qualquer maneira. Primeiro defina:

Object.defineProperty(
  self,
  'x',
  { get: function() { return self.y = self.y ? 0 : '0'; } }
);

Então nós temos

x != x // false

mas

x !== x // true
GOTO 0
fonte
9
Ha! :-) Mas é efetivamente foo() != foo()onde foo retorna 1 e 2. Por exemplo, os valores não são os mesmos, é apenas comparar valores diferentes.
TJ Crowder
2

Eu só quero apontar NaN não é a única coisa que produz x !== xsem usar o objeto global. Existem muitas maneiras inteligentes de desencadear esse comportamento. Aqui está um usando getters:

var i = 0, obj = { get x() { return i++; }};
with(obj) // force dynamic context, this is evil. 
console.log(x === x); // false

Como outras respostas apontam, == executa coerção de tipo, mas como em outros idiomas e pelo padrão - NaN indica uma falha de computação e, por boas razões, não é igual a si mesmo.

Por alguma razão, além de mim, as pessoas consideram isso um problema com o JS, mas a maioria das linguagens que possuem duplas (a saber, C, Java, C ++, C #, Python e outras) exibe esse comportamento exato e as pessoas estão bem com isso.

Benjamin Gruenbaum
fonte
2
Sim, é exatamente isso que o @TJCrowder mencionou no comentário para a resposta do GOTO_0, não é?
Giorgi Nakeuri
Você poderia esclarecer como obter a coerção ambígua do tipo nesses outros idiomas?
Chicocvenancio
0

Como às vezes as imagens são melhores que as palavras, verifique esta tabela (que é o motivo para eu fazer disso uma resposta, em vez de um comentário, porque obtém uma melhor visibilidade).

Lá você pode ver que a comparação estrita de igualdade (===) só retorna true se tipo e conteúdo corresponderem, portanto

var f = "-1" === -1; //false

Enquanto a comparação de igualdade abstrata (==) verifica apenas o conteúdo *, convertendo tipos e comparando-os estritamente:

var t = "-1" == -1; //true

Embora não seja claro, sem consultar a ECMA , o que o JavaScript considera ao comparar, de uma maneira que o código abaixo é avaliado como verdadeiro.

 var howAmISupposedToKnowThat = [] == false; //true
MVCDS
fonte