Sabemos que existem dois comparadores de igualdade no Javascript:
==
e ===
, ou como são chamados: double equals e triple equals.Vamos dissecar aqui o funcionamento desses dois operadores e entender se um é melhor que o outro.
As mesmas regras explicadas são válidas para os comparadores de desigualdade
!=
e !==
.Triple Equals
Tecnicamente chamado de strict equality comparison (comparação de igualdade estrita) retorna
true
caso os dois operandos sejam iguais. Esse operador usa o algoritmo de comparação de igualdade estrita. Entende-se por iguais aqueles que são do mesmo tipo e possuem o mesmo valor.- Se os operandos foram de tipos diferentes, retorna
1 === '1' // false
O primeiro operando tem o tipo Number, e o segundo, String
- Se ambos os operandos forem um objeto, retorna
true
apenas se eles se referem ao mesmo objeto
const first = { b: 'b' } const second = first first === second // true { a: 'a' } === { a: 'a' } // false
No segundo exemplo, apesar de ambos os objetos serem aparentemente iguais, eles foram criados em endereços de memória distintos, logo, do ponto de vista de igualdade estrita, são diferentes - ver Value Types and Reference Types
- Se ambos os operandos forem
null
ouundefined
, retornatrue
null === null // true undefined === undefined // true
- Se ambos os operandos forem
NaN
, retornafalse
- Caso contrário, os valores dos operandos são comparados:
- Números devem ter o mesmo valor numérico.
+0
e-0
são considerados mesmo valor. - Strings devem ter os mesmos caracteres e na mesma ordem
- Booleans devem ser ambos
true
oufalse
Double Equals
Tecnicamente chamado de abstract equality comparison (comparação de igualdade abstrata), e também conhecido como loose equality, diferentemente do triple equals, esse comparador tenta converter operandos de diferentes tipos antes de efetuar a comparação.
Esse operador usa o algoritmo de comparação abstrata (ou ver ES6) que vamos detalhar abaixo.
Vamos considerar que estamos comparando
x
e y
, onde ambos podem ser de qualquer tipo.- Caso os operandos forem do mesmo tipo, o Javascript irá simplesmente usar o operador triple equals. Logo
2 == 2
é o mesmo de2 === 2
pois ambos são números (tipoNumber
).
Porém, caso sejam de tipos diferentes, a regra para decidir qual operando converter depende dos tipos do operando.
- Se ambos os operandos forem
null
ouundefined
, retornatrue
- Se estivermos comparando uma
String
com umNumber
, a string é convertida para número e então o operador é chamado novamente.
1 == '1' // 1 == ToNumber('1') // 1 == 1 // true
- Se estivermos comparando um
Boolean
com qualquer outro tipo, o boolean é convertido para número e então o operador é chamado novamente
true == 1 // ToNumber(true) == 1 // 1 == 1 // true true == 2 // ToNumber(true) == 2 // 1 == 2 // false
O resultado do primeiro exemplo é
true
pois a conversão de um Boolean
true
para número é sempre igual 1
. Por outro lado false
é igual a +0
- Se um dos operandos for do tipo
String
,Number
ouSymbol
, e o outro forObject
, o objeto é convertido para primitivo e então o operador é chamado novamente
{ a: 'hello' } == '5' // ToPrimitive({ a: 'hello' }) == '5' // '[object Object]' == '5' // false [1,2,3] == '5' // ToPrimitive([1,2,3]) == '5' // '1,2,3' == '5' // false
Caso a comparação não se encaixe em nenhum cenário acima, retorna
false
Objects To Primitive
Ao converter objetos para primitivos, dois métodos podem ser usados:
valueOf
e toString
. Por padrão, o método valueOf
do objeto é usado primeiro. Caso retorne um primitivo - ver Primitives - esse valor será usado. Uma segunda tentativa será realizada, dessa vez com o método
toString
. Caso retorne um primitivo, esse valor será usado.Caso nenhum dos dois métodos retorne um valor primitivo, uma exceção será disparada.
const object = { a: 'hello' } object == '1' // ToPrimitive(object) == '1' // object.valueOf() // Retorna um objeto {} // object.toStirng() // retorna uma string '[object Object]' // '[object Object]' == '1' // false
const object = { a: 'hello' } object.toString = () => ({}) object == '1' // ToPrimitive(object) == '1' // object.valueOf() // Retorna um objeto {} // object.toStirng() // Retorna um objeto {} pois alteramos o método
O exemplo acima irá disparar uma exceção, pois nenhum dos dois métodos (
valueOf
e toString
) retornou um primitivo.Para objetos do tipo
Date
, a ordem dos métodos é toString
e valueOf
Conclusão
Com todas essas variáveis a se considerar, é melhor usarmos sempre o operador
===
, pois não precisamos nos preocupar com tantas regras e observações, fora que seu uso torna a leitura e entendimento mais natural.