Por que 2 == em JavaScript?

Eu descobri recentemente que 2 == [2] em JavaScript. Acontece que essa peculiaridade tem algumas conseqüências interessantes:

 var a = [0, 1, 2, 3]; a[[2]] === a[2]; // this is true 

Da mesma forma, os seguintes trabalhos:

 var a = { "abc" : 1 }; a[["abc"]] === a["abc"]; // this is also true 

Ainda mais estranho, isso também funciona:

 [[[[[[[2]]]]]]] == 2; // this is true too! WTF? 

Esses comportamentos parecem consistentes em todos os navegadores.

Alguma ideia do porquê este é um recurso de linguagem?

Aqui estão as conseqüências mais insanas desse “recurso”:

 [0] == false // true if ([0]) { /* executes */ } // [0] is both true and false! var a = [0]; a == a // true a == !a // also true, WTF? 

Estes exemplos foram encontrados por jimbojw http://jimbojw.com fama, bem como walkeyerobot .

Pode consultar o algoritmo de comparação na especificação ECMA (as secções relevantes da ECMA-262, 3ª edição para o seu problema: 11.9.3, 9.1, 8.6.2.6).

Se você traduzir os algoritmos abstratos envolvidos de volta para JS, o que acontece quando avaliar 2 == [2] é basicamente isto:

 2 === Number([2].valueOf().toString()) 

onde valueOf() para matrizes retorna o próprio array e a representação de string de um array de um elemento é a representação de string do único elemento.

Isso também explica o terceiro exemplo como [[[[[[[2]]]]]]].toString() ainda é apenas a string 2 .

Como você pode ver, há muita mágica por trás da cena envolvida, e é por isso que geralmente uso apenas o operador de igualdade estrito === .

O primeiro e o segundo exemplo são mais fáceis de seguir, pois os nomes das propriedades são sempre strings, então

 a[[2]] 

é equivalente a

 a[[2].toString()] 

que é apenas

 a["2"] 

Lembre-se de que até as chaves numéricas são tratadas como nomes de propriedades (ou seja, cadeias de caracteres) antes de qualquer matriz mágica acontecer.

É por causa da conversão de tipo implícito do operador == .

[2] é convertido para Number é 2 quando comparado com um Number. Tente o operador unário + em [2].

 > +[2] 2 
 var a = [0, 1, 2, 3]; a[[2]] === a[2]; // this is true 

No lado direito da equação, temos o [2], que retorna um tipo de número com valor 2. À esquerda, estamos criando primeiro um novo array com um único object de 2. Então, estamos chamando um [( array está aqui)]. Não tenho certeza se isso é avaliado como uma string ou um número. 2 ou “2”. Vamos pegar o primeiro caso. Eu acredito que um [“2”] criaria uma nova variável e retornaria null. null! == 2. Então, vamos supor que, na verdade, está implicitamente convertendo para um número. a [2] retornaria 2. 2 e 2 correspondem no tipo (assim === funciona) e valor. Eu acho que é implicitamente converter a matriz para um número porque um [valor] espera uma string ou número. Parece que o número tem maior precedência.

Em uma nota lateral, eu me pergunto quem determina essa precedência. É porque [2] tem um número como primeiro item, então ele converte em um número? Ou será que, ao passar um array para um [array], ele tenta transformar o array em um número primeiro, depois em string. Quem sabe?

 var a = { "abc" : 1 }; a[["abc"]] === a["abc"]; 

Neste exemplo, você está criando um object chamado a com um membro chamado abc. O lado direito da equação é bem simples; é equivalente a a.abc. Isso retorna 1. O lado esquerdo primeiro cria um array literal de [“abc”]. Em seguida, você procura uma variável no object a, passando a matriz recém-criada. Como isso espera uma string, ela converte a matriz em uma string. Isso agora avalia para um [“abc”], que é igual a 1. 1 e 1 são do mesmo tipo (é por isso que === funciona) e igual valor.

 [[[[[[[2]]]]]]] == 2; 

Esta é apenas uma conversão implícita. === não funcionaria nessa situação porque há uma incompatibilidade de tipo.

Para o caso == , é por isso que Doug Crockford recomenda sempre usar === . Não faz nenhuma conversão de tipo implícita.

Para os exemplos com === , a conversão de tipo implícito é feita antes que o operador de igualdade seja chamado.

 [0] == false // true if ([0]) { /* executes */ } // [0] is both true and false! 

Isso é interessante, não é que [0] seja verdadeiro e falso, na verdade

 [0] == true // false 

É uma maneira engraçada do javascript de processar o operador if ().

Uma matriz de um item pode ser tratada como o próprio item.

Isso se deve à digitação de pato. Desde “2” == 2 == [2] e possivelmente mais.

Para adicionar um pequeno detalhe às outras respostas … ao comparar um Array com um Number , o Javascript converterá o Array com parseFloat(array) . Você pode testá-lo no console (por exemplo, Firebug ou Web Inspector) para ver quais valores diferentes de Array são convertidos.

 parseFloat([2]); // 2 parseFloat([2, 3]); // 2 parseFloat(['', 2]); // NaN 

Para Array s, parseFloat executa a operação no primeiro membro da Array e descarta o restante.

Edit: detalhes de Per Christoph, pode ser que ele está usando o formulário mais longo internamente, mas os resultados são consistentemente idênticos ao parseFloat , assim você sempre pode usar parseFloat(array) como taquigrafia para saber com certeza como ele será convertido.

Você está comparando 2 objects em cada caso .. Não use ==, se você está pensando em comparação, você está tendo === em mente e não ==. == pode muitas vezes dar efeitos insanos. Procure as partes boas na linguagem 🙂

Explicação para a seção EDIT da pergunta:

1º exemplo

 [0] == false // true if ([0]) { /* executes */ } // [0] is both true and false! 

Primeiro typecast [0] para um valor primitivo de acordo com a resposta de Christoph acima, temos “0” ( [0].valueOf().toString() )

 "0" == false 

Agora, typecast Boolean (false) para Number e String (“0”) para Number

 Number("0") == Number(false) or 0 == 0 so, [0] == false // true 

Quanto à instrução if , se não houver uma comparação explícita na condição if, a condição será avaliada para valores verdadeiros .

Existem apenas 6 valores falsos: false, null, undefined, 0, NaN e string vazia “”. E qualquer coisa que não seja um valor falso é um valor absoluto.

Como [0] não é um valor falso, é um valor verdadeiro, a instrução if avaliada como verdadeira e executa a instrução.


2º exemplo

 var a = [0]; a == a // true a == !a // also true, WTF? 

Novamente digite casting os valores para primitivo,

  a = a or [0].valueOf().toString() == [0].valueOf().toString() or "0" == "0" // true; same type, same value a == !a or [0].valueOf().toString() == [0].valueOf().toString() or "0" == !"0" or "0" == false or Number("0") == Number(false) or 0 = 0 // true