Duas formas de perguntar "esses valores são iguais?"
O JavaScript te dá dois operadores de igualdade: === (estrito) e == (solto). Parecem quase a mesma coisa, mas o comportamento é bem diferente.
=== compara se os dois operandos têm o mesmo tipo e o mesmo valor. Já o == primeiro converte os operandos para um tipo em comum e depois compara. Essa conversão — a famosa coerção de tipos — é onde mora boa parte da fama ruim da igualdade em JavaScript.
Resumindo: use === como padrão. Mas vale a pena ler a versão longa pelo menos uma vez, pra entender do que você está se livrando.
Igualdade estrita: o que você quase sempre quer
O === só retorna true quando os dois lados batem em tipo e valor. Sem coerção, sem pegadinha:
Se os tipos forem diferentes, o resultado já é false na hora. Se forem iguais, o JavaScript compara os valores. Para primitivos, é uma comparação de valor; para objetos, é uma comparação de referência (já chegamos lá).
O !== é o operador de desigualdade estrita e segue as mesmas regras, só que ao contrário.
Igualdade solta: coerção disfarçada
O == permite que os dois lados tenham tipos diferentes. Antes de comparar, ele faz um verdadeiro malabarismo de coerção de tipos para tentar igualá-los:
As regras exatas estão na spec, e até que não são terríveis, mas lembrar delas no meio de um debug é outra história. "0" == false dar true pega até desenvolvedor experiente. O mesmo vale para [] == false (que também é true, porque o array é coagido para "", que por sua vez vira 0).
Por isso a maioria dos guias de estilo — e a regra eqeqeq do ESLint — recomenda usar === por padrão. Você digita um caractere a mais em troca de regras que dá pra memorizar de verdade.
O único uso útil do ==
Existe um idioma com igualdade solta que vale a pena conhecer: x == null retorna true quando x é null ou undefined, e false para qualquer outra coisa.
O equivalente estrito seria x === null || x === undefined, que até funciona, mas fica mais poluído visualmente. Muitas bases de código abrem uma exceção e liberam o == null como o único caso permitido. Escolha uma regra e siga à risca.
Como comparar objetos em JavaScript: igualdade por referência
Quando o assunto é objetos, arrays e funções, tanto === quanto == fazem a mesma pergunta: "os dois lados apontam para o mesmo objeto na memória?" E não "os dois têm o mesmo conteúdo?".
Dois objetos literais com o mesmo conteúdo continuam sendo dois objetos distintos. Isso pega todo mundo desprevenido pelo menos uma vez.
Pra fazer uma comparação baseada em valor, você precisa escrever seu próprio helper, usar uma biblioteca (como lodash.isequal), ou serializar com JSON.stringify no caso de objetos simples:
JSON.stringify só funciona para dados simples — ele ignora funções, undefined, símbolos e, dependendo do formato do objeto, não garante a ordem das chaves entre engines diferentes. Serve como um check rápido, mas não é uma solução geral.
NaN não é igual a nada em JavaScript
NaN ("not a number") é o valor que o JavaScript devolve quando uma operação numérica não tem resposta que faça sentido — 0/0, Number("abc"), Math.sqrt(-1). Os dois operadores de igualdade retornam false sempre que um dos lados for NaN, mesmo quando os dois lados são NaN:
Para detectar NaN, use Number.isNaN(value). Evite o antigo isNaN global, porque ele faz coerção do argumento antes da checagem — ou seja, isNaN("hello") retorna true, o que raramente é o comportamento desejado.
Object.is: quase igual ao ===, com dois ajustes
Object.is(a, b) se comporta como ===, exceto em dois casos específicos:
Na maioria das vezes, o === é o que você quer. Recorra ao Object.is apenas quando precisar tratar NaN como igual a si mesmo ou diferenciar +0 de -0 — ambos casos raros, mas ocasionalmente importantes em código numérico e nas entranhas de frameworks (o React, por exemplo, usa Object.is para comparar estados).
Operadores de desigualdade seguem a mesma lógica
O !== é estrito e o != é com coerção. A recomendação é a mesma dos operadores de igualdade:
Por padrão, prefira !==. Se seu guia de estilo já permite == null, faz sentido permitir também o equivalente != null para checar "não é null nem undefined".
Um checklist para comparar valores
Sempre que precisar comparar dois valores, passe por esta lista:
- São primitivos do mesmo tipo? Use
===. - Quer checar null ou undefined?
x == nullresolve, se seu guia de estilo liberar; caso contrário, escrevax === null || x === undefined. - Quer detectar
NaN?Number.isNaN(x). - Comparar objetos por identidade (mesma referência)?
===faz exatamente isso. - Comparar objetos pelo conteúdo? Escreva uma função auxiliar, use uma lib ou serialize os dois. Os operadores nativos não dão conta.
Mantenha o foco no ===, trate o == como ferramenta específica para o caso == null e você vai fugir das pegadinhas de igualdade que lotam os FAQs de JavaScript.
A seguir: operadores
A igualdade é só uma fatia do conjunto de operadores do JavaScript. O próximo conteúdo cobre o restante — aritméticos, lógicos, de atribuição e as versões abreviadas que você vai usar direto no dia a dia.
Perguntas frequentes
Qual a diferença entre == e === em JavaScript?
O === é a igualdade estrita: só retorna true se os dois operandos tiverem o mesmo tipo e o mesmo valor. Já o == é a igualdade frouxa, que faz coerção de tipo antes de comparar. Por isso 1 === '1' dá false, mas 1 == '1' dá true — nesse caso, o == converte a string em número antes de comparar.
Devo sempre usar === em JavaScript?
Como regra geral, sim. O === é previsível e as regras são simples de memorizar. A exceção mais comum é x == null, que cobre tanto null quanto undefined de uma tacada só. A maioria dos linters (como a regra eqeqeq do ESLint) exige o === e permite esse padrão como exceção configurável.
Por que NaN === NaN retorna false?
Por definição da IEEE 754, NaN não é igual a nada — nem a ele mesmo. Ou seja, qualquer operador de igualdade envolvendo NaN retorna false. Para testar se um valor é NaN, use Number.isNaN(x) ou então Object.is(NaN, NaN), que retorna true.
Como comparar dois objetos por igualdade em JavaScript?
Tanto == quanto === comparam objetos por referência, não pelo conteúdo. Por isso {a: 1} === {a: 1} dá false — são dois objetos diferentes na memória. Para comparar pelo valor, você precisa escrever sua própria função, usar algo como isEqual do Lodash, ou serializar com JSON.stringify se forem objetos simples.