Como todos sabemos, == em JavaScript é uma operação relativamente complexa. Suas regras de operação são muito estranhas e podem cometer erros facilmente, tornando -o um dos "piores recursos" no JavaScript.
Com base na leitura cuidadosa da especificação do ECMAScript, desenhei uma imagem. Eu pensei que, depois de entender essa foto, você entenderá completamente tudo sobre a operação ==. Ao mesmo tempo, tentei provar a todos através deste artigo que == não é tão ruim assim. É fácil de dominar e até parece razoável e não tão ruim assim.
Primeiro, a imagem:
== A descrição exata das regras de operação está aqui: o algoritmo de comparação de igualdade abstrato. No entanto, com uma descrição tão complicada, você tem certeza de que não se sentirá tonto depois de lê -la? Você pode usá -lo para orientar sua prática imediatamente?
Certamente não funciona. Afinal, a especificação é para desenvolvedores do ambiente de execução de JavaScript (em comparação com os desenvolvedores de mecanismos V8), não para usuários de idiomas. A figura acima traduz a especificação em um formulário que é conveniente para todos verem.
Antes de apresentar cada parte na Figura 1 em detalhes, vamos revisar o conhecimento sobre os tipos de JS:
Existem dois tipos de valores em JS: tipo básico e tipo de objeto.
Os tipos básicos incluem: indefinido, nulo, booleano, número e string.
Tanto o tipo indefinido quanto o tipo nulo têm apenas um valor, ou seja, indefinido e nulo; O tipo booleano tem dois valores: verdadeiro e falso; Existem muitos valores do tipo de número; e o tipo de string possui inúmeros valores (teoricamente).
Todos os objetos têm métodos de valueof () e tostring (), que são herdados do objeto e podem, obviamente, ser reescrito por subclasses.
Agora considere a expressão:
x == y
onde x e y são valores de um dos seis tipos.
Quando os tipos de xey são os mesmos, x == y pode ser convertido em x === y, e o último é muito simples (a única coisa a observar é NAN), portanto, abaixo, consideramos apenas os casos em que os tipos de x e y são diferentes.
1. Tenha e nenhum
Na Figura 1, seis tipos de valores de JavaScript são representados por retângulos com fundo azul. Primeiro eles são divididos em dois grupos:
String, número, booleano e objeto (correspondendo à grande caixa retangular à esquerda)
Indefinido e nulo (correspondente à caixa retangular à direita)
Qual é a base para o agrupamento? Vamos dar uma olhada. Indefinido e nulo à direita são usados para indicar incerteza, não ou vazia, enquanto os quatro tipos à direita são todos determinados, existentes e não vazios. Podemos dizer isso:
À esquerda, há um mundo de existência e, à direita, há um mundo vazio.
Portanto, é razoável comparar qualquer valor nos dois mundos com false. (isto é, a linha horizontal que conecta dois retângulos na Figura 1 é marcada falsa)
2. Vazio e vazio
Indefinido e nulo em JavaScript é outro lugar que geralmente nos trava. Geralmente é considerado uma falha de design, na qual não vamos cavar. Mas ouvi dizer que o autor de JavaScript pensou inicialmente o seguinte:
Se você planeja atribuir uma variável ao valor do tipo de objeto, mas ainda não atribuiu um valor, poderá usar o NULL para representar o estado neste momento (uma das evidências é que o resultado do tipo de nulo é 'objeto'); Pelo contrário, se você planeja atribuir uma variável ao valor do tipo original, mas ainda não atribuiu um valor, poderá usar indefinido para representar o estado neste momento.
Independentemente de esse boato ser credível ou não, é razoável que o resultado da comparação entre os dois seja verdadeiro. (ou seja, verdadeiro marcado na linha vertical à direita na Figura 1)
Antes de passar para a próxima etapa, vamos falar sobre os dois símbolos na Figura 1: letras maiúsculas N e P. Esses dois símbolos não significam positivo e negativo na seção PN. Em vez de:
N representa a operação de tonumber, o que significa que converte o operando em um número. É uma operação abstrata na especificação ES, mas podemos usar a função número () em JS para substituí -la equivalentemente.
P representa a operação tominitiva, ou seja, convertendo o operando no valor do tipo original. É também uma operação abstrata na especificação ES e também pode ser traduzida em código JS equivalente. Mas é um pouco mais complicado. Para simplificar, para um objeto obj:
TopEnmitive (OBJ) é equivalente a: primeiro calcule obj.valueof (), se o resultado for o valor original, esse resultado será retornado; Caso contrário, obj.toString () será calculado e, se o resultado for o valor original, esse resultado será retornado; Caso contrário, uma exceção é lançada.
NOTA: Há uma exceção aqui, ou seja, um objeto de data do tipo, que primeiro chamará o método ToString ().
Na Figura 1, uma linha marcada N ou P indica que, quando os dois tipos de dados é conectada para executar a operação ==, o operando no lado marcado N ou P deve primeiro executar o tonumber ou a transformação tominitiva.
3. Verdadeiro e falso
Como pode ser visto na Figura 1, quando um valor booleano é comparado com outros tipos de valores, o valor booleano é convertido em um número. Especificamente
Verdadeiro -> 1
Falso -> 0
Isso não requer muito abuso verbal. Pense nisso, em C, não há tipo booleano. Os números inteiros 1 e 0 são geralmente usados para representar a lógica verdadeira ou falsa.
4. Sequência de caracteres
Na Figura 1, dividimos a corda e o número em um grupo. Por que? Entre os seis tipos, a corda e o número estão seqüências de caracteres (pelo menos literalmente). Uma string é uma sequência de todos os caracteres legais, enquanto um número pode ser considerado como uma sequência de caracteres que atendem a certas condições. Portanto, os números podem ser considerados como um subconjunto de cordas.
De acordo com a Figura 1, ao executar a operação == de strings e números, você precisa usar a operação de tonumber para converter a string em números. Supondo que x seja uma string e y é um número, então:
x == y -> número (x) == y
Então, qual é a regra para converter seqüências em números? A especificação é descrita de uma maneira muito complicada, mas, de um modo geral, é para remover as citações de ambos os lados da corda e ver se ela pode formar um número legal. Nesse caso, o resultado da conversão é esse número; Caso contrário, o resultado é NAN. Por exemplo:
Número ('123') // Resultado 123
Número ('1.2E3') // Resultado 1200
Número ('123ABC') // Result Nan
Obviamente, há exceções, como o resultado de converter uma string vazia em um número é 0. No momento
Número ('') // resultado 0
V. simples e complexo
Tipos primitivos são tipos simples, eles são diretos e fáceis de entender. No entanto, a desvantagem é que a capacidade de expressão é limitada e difícil de expandir; portanto, existem objetos. Um objeto é uma coleção de atributos, e o atributo em si pode ser um objeto. Portanto, os objetos podem ser construídos arbitrariamente complexos o suficiente para representar várias coisas.
Mas às vezes as coisas são complicadas e não é uma coisa boa. Por exemplo, nem todo mundo tem tempo, paciência ou a necessidade de lê -lo do começo ao fim. Geralmente basta entender apenas seus pensamentos centrais. Portanto, o artigo possui palavras -chave e visões gerais. O mesmo vale para objetos em JavaScript. Precisamos ter um meio de entender suas principais características, para que os objetos tenham métodos tostring () e valueof ().
O método tostring () é usado para obter uma descrição de texto do objeto; e o método ValueOf () é usado para obter o valor próprio do objeto.
Claro, este é apenas o meu próprio entendimento. Além disso, como o nome indica, o método tostring () tende a retornar uma string. E o método ValueOf ()? De acordo com a descrição da especificação, ela tende a retornar um número - embora no tipo interno, o método ValueOf () retorna apenas número e data.
De acordo com a Figura 1, quando um objeto é comparado com um não-objeto, o objeto precisa ser convertido em um tipo primitivo (embora, ao comparar com um tipo booleano, o tipo booleano precisa ser convertido em um tipo numérico primeiro, mas o tipo de objeto precisa ser convertido em um tipo primitivo a seguir). Isso também é razoável. Afinal, == não é estritamente igual comparação. Precisamos apenas retirar os principais recursos do objeto para participar da operação e colocar os recursos secundários de lado.
Seis. Tudo é contado
Vamos olhar para a Figura 1. As linhas marcadas N ou P dentro não têm direção. Se marcarmos as setas nessas linhas, os pontos de conexão do final marcados N ou P para o outro extremo, então ficaremos (não considerando indefinidos e nulos):
Você descobriu alguma coisa? Sim, durante o processo de cálculo, todos os tipos de valores tendem a converter em tipos numéricos. Afinal, uma celebridade disse uma vez:
Tudo é contado.
7. Apenas me dê uma castanha
Há muita bobagem no passado, então aqui está um exemplo para provar que a Figura 1 é realmente conveniente e eficaz para orientar a prática.
Exemplo, calcule o seguinte:
[''] == false
Primeiro, os dois operandos são do tipo de objeto e tipo booleano, respectivamente. De acordo com a Figura 1, é necessário converter o tipo booleano em um tipo numérico, e o resultado da conversão falsa em um numérico é 0, então a expressão se torna:
[''] == 0
Os dois operandos se tornam tipo de objeto e tipo numérico. De acordo com a Figura 1, o tipo de objeto precisa ser convertido para o tipo original:
Primeiro, ligue para [] .Valueof (). Como o método ValueOf () da matriz retorna, o resultado não é o tipo original. Continue chamando [] .ToString ().
Para matrizes, o algoritmo do método tostring () é converter cada elemento em um tipo de string e, em seguida, concatená -lo por sua vez com '', então o resultado final é uma sequência vazia, que é um valor do tipo original.
Neste ponto, a expressão se torna:
'' == 0
Os dois operandos se tornam tipos de string e tipos numéricos. De acordo com a Figura 1, o tipo de string precisa ser convertido em tipos numéricos. Como mencionado anteriormente, a sequência vazia se torna um número de 0. Portanto, a expressão se torna:
0 == 0
Até agora, os tipos dos dois operando são finalmente os mesmos, e o resultado é obviamente verdadeiro.
A partir deste exemplo, podemos ver que, para dominar as regras da operação ==, além de lembrar a Figura 1, também precisamos lembrar as regras dos métodos ToString () e ValueOf () desses objetos internos. Incluindo objeto, matriz, data, número, string, booleano, etc.
8. Vamos resumir
A declaração anterior é muito confusa. Aqui vou resumir as regras de == operação expressa na Figura 1:
O resultado de indefinido == null é verdadeiro. O resultado de sua comparação com todos os outros valores é falso.
Quando uma string == número, a sequência é convertida em um número.
Valor booleano == Quando outros tipos são usados, o valor booleano é convertido em um número.
Quando um objeto == numérico/string, o objeto é convertido em um tipo primitivo.
Finalmente, mudei a imagem apenas para entretenimento :)
OK, acabou. Se você acha que este artigo é útil para você, goste para que mais pessoas possam vê -lo.
Além disso, aponte as falácias no artigo.