Em JavaScript, The Good Parts , Douglas Crockford escreveu:
O JavaScript possui dois conjuntos de operadores de igualdade:
===
e!==
, e seus gêmeos maus==
e!=
. Os bons funcionam da maneira que você esperaria. Se os dois operandos forem do mesmo tipo e tiverem o mesmo valor,===
produztrue
e!==
produzfalse
. Os gêmeos maus fazem a coisa certa quando os operandos são do mesmo tipo, mas se são de tipos diferentes, tentam coagir os valores. As regras pelas quais eles fazem isso são complicadas e imemoráveis. Estes são alguns dos casos interessantes:'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
A falta de transitividade é alarmante. Meu conselho é nunca usar os gêmeos do mal. Em vez disso, sempre use
===
e!==
. Todas as comparações mostradas são produzidasfalse
com o===
operador.
Dada essa observação inequívoca, existe um momento em que o uso ==
pode ser realmente apropriado?
fonte
==
por padrão,===
se destaca e me informa que algo importante está acontecendo.Respostas:
Eu vou fazer um argumento para
==
Douglas Crockford, que você citou, é conhecido por suas muitas e muitas vezes muito úteis opiniões. Enquanto estou com Crockford, nesse caso em particular, vale a pena mencionar que não é a única opinião. Existem outros, como o criador da linguagem Brendan Eich, que não vêem o grande problema
==
. O argumento é mais ou menos o seguinte:JavaScript é uma linguagem de tipo comportamental *. As coisas são tratadas com base no que elas podem fazer e não no seu tipo real. É por isso que você pode chamar o
.map
método de uma matriz em um NodeList ou em um conjunto de seleção jQuery. É também por isso que você pode fazer3 - "5"
e obter algo significativo de volta - porque "5" pode agir como um número.Ao executar uma
==
igualdade, você está comparando o conteúdo de uma variável em vez de seu tipo . Aqui estão alguns casos em que isso é útil:.value
de um elemento de entrada no DOM? Sem problemas! Você não precisa começar a transmiti-lo ou se preocupar com o seu tipo - você pode==
imediatamente obter números e obter algo significativo de volta.== null
, uma vez quenull
representa comportamentalmente não há nada lá e indefinido também não tem nada lá.==
argumento, ele tratará os casos em que o usuário não inseriu nada ou apenas espaço em branco para você, o que provavelmente é o que você precisa.Vamos dar uma olhada nos exemplos de Crockford e explicá-los comportamentalmente:
Basicamente, ele
==
foi projetado para funcionar com base em como as primitivas se comportam no JavaScript, não com base no que são . Embora eu pessoalmente não concorde com esse ponto de vista, definitivamente há mérito em fazê-lo - especialmente se você adotar esse paradigma de tratamento de tipos com base no comportamento em toda a linguagem.* alguns podem preferir o nome de tipagem estrutural, que é mais comum, mas há uma diferença - não está realmente interessado em discutir a diferença aqui.
fonte
==
não é que nenhuma de suas comparações seja útil , é que as regras são impossíveis de serem lembradas, portanto você quase cometerá erros. Por exemplo: "Precisa verificar se você recebeu uma entrada significativa de um usuário?", Mas '0' é uma entrada significativa e'0'==false
verdadeira. Se você tivesse usado,===
teria que pensar explicitamente sobre isso e não teria cometido o erro.3 - "5"
exemplo levanta um bom ponto por si só: mesmo se você usar exclusivamente===
para comparação, é assim que as variáveis funcionam em Javascript. Não há como escapar completamente.==
no meu próprio código, acho um anti-padrão e concordo completamente que o algoritmo de igualdade abstrata é difícil de lembrar. O que estou fazendo aqui é argumentar com as pessoas.Acontece que o jQuery usa a construção
extensivamente, como uma abreviação para o código equivalente:
Isso é uma conseqüência da Especificação de Linguagem ECMAScript § 11.9.3, O Algoritmo de Comparação de Igualdade Abstrata , que declara, entre outras coisas, que
e
Essa técnica específica é comum o suficiente para que o JSHint tenha um sinalizador projetado especificamente para ela.
fonte
== null or undefined
é o único lugar onde eu não uso===
ou!==
==
e apenas é usado===
nos casos em que é necessário: github.com/madrobby/zepto/blob/master/src/event.js__proto__
e, por sua vez, forçá-lo quase que sozinho na especificação da linguagem para evitar a quebra de sites móveis.Verificar valores para
null
ouundefined
é uma coisa, como foi explicado em abundância.Há outra coisa, onde
==
brilha:Você pode definir a comparação da seguinte
>=
forma (as pessoas geralmente começam,>
mas acho isso mais elegante):a > b
<=>a >= b && !(b >= a)
a == b
<=>a >= b && b >= a
a < b
ea <= b
são deixados como um exercício para o leitor.Como sabemos, em JavaScript
"3" >= 3
e"3" <= 3
de onde você obtém3 == "3"
. Você pode dizer que é uma péssima idéia permitir a comparação de comparação entre seqüências e números analisando a sequência. Mas, como é assim que funciona,==
é absolutamente a maneira correta de implementar esse operador de relacionamento.Portanto, a coisa realmente boa
==
é que é consistente com todos os outros relacionamentos. Em outras palavras, se você escrever isso:Você
==
já está usando implicitamente .Agora, para a questão bastante relacionada: Foi uma má escolha implementar a comparação de números e cadeias de caracteres da maneira como é implementada? Visto isoladamente, parece uma coisa estúpida de se fazer. Mas no contexto de outras partes do JavaScript e do DOM, é relativamente pragmático, considerando o seguinte:
Object
para ter um mapa esparso de ints a valores)input[type=number]
)Por várias razões, fazia sentido que as cordas se comportassem como números quando necessário. E supondo que a comparação e concatenação de cadeias
::
possuam operadores diferentes (por exemplo, para concating e um método para comparar (onde você pode usar todos os tipos de parâmetros relacionados à distinção entre maiúsculas e minúsculas e outras)), isso seria realmente uma bagunça. Mas essa sobrecarga de operador provavelmente é de fato a origem do "Java" em "JavaScript";)fonte
>=
não é realmente transitivo. É bem possível no JS que nema > b
nema < b
nemb == a
(por exemploNaN
:).+
não é realmente comutativo, porqueNaN + 5 == NaN + 5
não se sustenta. O ponto é que>=
trabalha com valores numéricos para os quais==
trabalha de maneira consistente. Não deveria ser surpresa que "não é um número" é, por sua própria natureza, não número-ish;)==
é consistente com o mau comportamento de>=
? Ótimo, agora eu gostaria que houvesse uma>==
...>=
é bastante consistente com o restante das APIs de linguagem / padrão. Na sua totalidade, o JavaScript consegue ser mais do que a soma de suas partes peculiares. Se você gostaria de um>==
, você também gostaria de um rigoroso+
? Na prática, muitas dessas decisões facilitam muitas coisas. Então eu não me apressaria em julgá-los como pobres.>=
é significativo,==
é igualmente significativo - isso é tudo. Ninguém diz que você deve comparar[[]]
comfalse
. Em linguagens como C, o resultado desse nível de absurdo é um comportamento indefinido. Apenas trate da mesma maneira: não faça. E você ficará bem. Você também não precisará se lembrar de nenhuma regra mágica. E então é realmente bastante direto.Como matemático profissional, vejo no operador de uniformidade de Javscript
==
(também chamado de "comparação abstrata", "igualdade frouxa" ) uma tentativa de construir uma relação de equivalência entre entidades, que inclui ser reflexiva , simétrica e transitiva . Infelizmente, duas dessas três propriedades fundamentais falham:==
não é reflexivo :A == A
pode ser falso, por exemplo==
não é transitivo :A == B
eB == C
juntos não implicamA == C
, por exemplo,Somente a propriedade simétrica sobrevive:
A == B
implicaB == A
, que violação é provavelmente impensável em qualquer caso e levaria a uma rebelião séria;)Por que as relações de equivalência são importantes?
Porque esse é o tipo de relação mais importante e predominante, suportado por vários exemplos e aplicativos. A aplicação mais importante é a decomposição de entidades em classes de equivalência , que por si só é uma maneira muito conveniente e intuitiva de entender as relações. E a falta de equivalência leva à falta de classes de equivalência, o que, por sua vez, leva à falta de intuitividade e complexidade desnecessária que é bem conhecida.
Por que é uma péssima idéia escrever
==
para uma relação de não equivalência?Porque rompe nossa familiaridade e intuição, pois literalmente qualquer relação interessante de similaridade, igualdade, congruência, isomorfismo, identidade etc. é uma equivalência.
Conversão de tipo
Em vez de confiar em uma equivalência intuitiva, o JavaScript introduz a conversão de tipo:
Mas como é definida a conversão de tipo? Através de um conjunto de regras complicadas, com inúmeras exceções?
Tentativa de construir relação de equivalência
Booleanos. Claramente
true
efalse
não são iguais e devem estar em classes diferentes.Números. Felizmente, a igualdade de números já está bem definida, na qual dois números diferentes nunca estão na mesma classe de equivalência. Em matemática, é isso. Em JavaScript, a noção de número é um pouco deformada pela presença dos mais exóticos
-0
,Infinity
e-Infinity
. Nossa intuição matemática determina que0
e-0
deve estar na mesma classe (de fato-0 === 0
étrue
), enquanto que cada um dos infinitos é uma classe separada.Números e booleanos. Dadas as classes numéricas, onde colocamos booleanos?
false
torna-se semelhante a0
, enquantotrue
torna-se semelhante a,1
mas nenhum outro número:Existe alguma lógica aqui para colocar
true
junto com1
? É certo que1
se distingue, mas também é-1
. Eu pessoalmente não vejo qualquer razão para convertertrue
para1
.E fica ainda pior:
Então, de
true
fato, é convertido em1
entre todos os números! Isso é lógico? É intuitivo? A resposta é deixada como exercício;)Mas e quanto a isso:
O único booleano
x
com ox && true
sertrue
éx = true
. O que prova que ambos1
e2
(e qualquer outro número que0
) sejam convertidos paratrue
! O que mostra é que nossa conversão falha em outra propriedade importante - a bijeção . Significando que duas entidades diferentes podem ser convertidas na mesma. O que, por si só, não precisa ser um grande problema. O grande problema surge quando usamos essa conversão para descrever uma relação de "semelhança" ou "igualdade frouxa" do que queremos chamar. Mas uma coisa é clara - não será uma relação de equivalência e não será descrita intuitivamente por meio de classes de equivalência.Mas podemos fazer melhor?
Pelo menos matematicamente - definitivamente sim! Uma simples relação de equivalência entre booleanos e números poderia ser construída com apenas
false
e0
estar na mesma classe. Assimfalse == 0
seria a única igualdade frouxa não trivial.E as cordas?
Podemos cortar strings de espaços em branco no início e no final para converter em números, também podemos ignorar zeros na frente:
Portanto, temos uma regra simples para uma string - apare os espaços em branco e os zeros na frente. Ou obtemos um número ou uma sequência vazia; nesse caso, convertemos para esse número ou zero. Ou não obtemos um número; nesse caso, não convertemos e, portanto, não obtemos nova relação.
Dessa forma, poderíamos obter uma relação de equivalência perfeita no conjunto total de booleanos, números e seqüências de caracteres! Exceto que ... Os designers de JavaScript obviamente têm outra opinião:
Portanto, as duas seqüências para as quais os dois convertem
0
são subitamente semelhantes! Porque ou porque? De acordo com a regra, as strings são fracamente iguais exatamente quando são estritamente iguais! Não apenas essa regra quebra a transitividade como vemos, mas também é redundante! Qual o sentido de criar outro operador==
para torná-lo estritamente idêntico ao outro===
?Conclusão
O operador de igualdade frouxa
==
poderia ter sido muito útil se estivesse cumprindo algumas leis matemáticas fundamentais. Mas como infelizmente não, sua utilidade sofre.fonte
NaN
? Além disso, a menos que um formato de número específico seja imposto para comparação com cadeias, deve resultar em comparação não intuitiva de cadeias ou não transitividade.NaN
age como um cidadão ruim :-). A transatividade pode e deve ser mantida para qualquer comparação de equivalência, intuitiva ou não.Sim, deparei-me com um caso de uso, ou seja, quando você está comparando uma chave com um valor numérico:
Eu acho que é muito mais natural realizar a comparação
key == some_number
do que comoNumber(key) === some_number
ou comokey === String(some_number)
.fonte
Encontrei um aplicativo bastante útil hoje. Se você deseja comparar números preenchidos, como
01
números inteiros normais,==
funciona muito bem. Por exemplo:Isso poupa a remoção do 0 e a conversão para um número inteiro.
fonte
'04'-0 === 4
, ou possivelmenteparseInt('04', 10) === 4
+'01' === 1
'011' == 011 // false
no modo não estrito e SyntaxError no modo estrito. :)Eu sei que esta é uma resposta tardia, mas parece haver alguma confusão possível sobre
null
eundefined
, qual IMHO é o que faz o==
mal, mais ainda que a falta de transitividade, que já é ruim o suficiente. Considerar:O que isso significa?
p1
tem um supervisor cujo nome é "Alice".p2
tem um supervisor cujo nome é "Nenhum".p3
explicitamente, inequivocamente, não possui um supervisor .p4
pode ou pode ter um supervisor. Não sabemos, não nos importamos, não devemos saber (questão de privacidade?), Pois não é da nossa conta.Quando você usa,
==
está em conflitonull
e oundefined
que é totalmente impróprio. Os dois termos significam coisas completamente diferentes! Dizer que não tenho um supervisor simplesmente porque me recusei a dizer quem é meu supervisor está errado!Entendo que existem programadores que não se importam com essa diferença entre
null
e /undefined
ou optam por usar esses termos de maneira diferente. E se o seu mundo não usanull
eundefined
corretamente, ou você deseja dar sua própria interpretação com esses termos, que assim seja. Acho que não é uma boa ideia.Agora, a propósito, não tenho nenhum problema
null
eundefined
ambos são falsos! É perfeitamente bom dizere, em seguida,
null
eundefined
faria com que o código que processa a supervisor para ser ignorada. Isso está correto, porque não sabemos ou não temos um supervisor. Tudo bom. Mas as duas situações não são iguais . É por isso que==
está errado. Novamente, as coisas podem ser falsas e usadas no sentido de digitação de pato, o que é ótimo para linguagens dinâmicas. É JavaScript, Pythonic, Rubyish etc. adequados. Mas, novamente, essas coisas NÃO são iguais.E não me fale sobre a não-transitividade:
"0x16" == 10
,10 == "10"
mas não"10" == "0x16"
. Sim, o JavaScript é um tipo fraco. Sim, é coercitivo. Mas a coerção nunca deve se aplicar à igualdade.A propósito, Crockford tem opiniões fortes. Mas você sabe o que? Ele está correto aqui!
FWIW Entendo que existem e, pessoalmente, me deparei com situações em que
==
é conveniente! Como pegar entrada de string para números e, digamos, comparar com 0. No entanto, isso é hack. Você tem a conveniência de compensar um modelo impreciso do mundo.TL; DR: falsidade é um ótimo conceito. Não deve se estender à igualdade.
fonte
p5
... a situação detypeof(p5.supervisor) === typeof(undefined)
onde supervisor nem sequer existe como um conceito: D