== ou ===: comparação e igualdade em JavaScript
A tabela de coerção do ==, por que NaN não é igual a NaN e como objetos comparam por referência — com cada caso rodado no Node e o bug do if (x == false).
=== compara valor e tipo, sem converter nada. == converte antes de
comparar, seguindo um algoritmo de sete passos que quase ninguém decorou. Use
=== sempre — e leia o resto desta lição para saber diagnosticar o código dos
outros, que está cheio de ==.
A diferença não é estilo. Ela é a razão pela qual 0 == '' é verdadeiro,
[] == false é verdadeiro e null == 0 é falso, três resultados que não formam
nenhum padrão memorizável.
Conferir produto e etiqueta: valor e tipo
Dois potes podem ter o número 10 escrito neles, mas um guarda dez moedas e o
outro guarda a palavra “10” numa etiqueta. === confere o conteúdo e a etiqueta;
== tenta abrir os potes e transformar um deles antes de decidir se parecem
iguais.
Em termos técnicos, === faz igualdade estrita e == faz igualdade
abstrata com coerção. Na primeira tabela, preveja cada resultado olhando valor
e tipo separadamente. Depois confira a saída: quando a sua previsão depende de
uma conversão que você não pediu, essa é a comparação que merece ser reescrita.
const quantidadeDigitada = '3';
const quantidadeEmEstoque = 3;
console.log(quantidadeDigitada === quantidadeEmEstoque);
console.log(Number(quantidadeDigitada) === quantidadeEmEstoque);
console.log('PRIMEIRACOMPRA' === 'PRIMEIRACOMPRA');
console.log(199 !== 199.0);A primeira linha é false porque '3' é string e 3 é número. A quarta é
false porque em JavaScript não existe inteiro separado de decimal: 199 e
199.0 são literalmente o mesmo número. A conversão fica explícita na sua mão,
na segunda linha, que é exatamente onde ela deve estar.
O que o == faz antes de comparar
Quando os tipos são diferentes, o == aplica uma sequência de conversões:
null e undefined são iguais entre si e a mais nada; booleano vira número;
string comparada com número vira número; objeto vira valor primitivo.
console.log('3' == 3);
console.log(0 == '');
console.log(0 == '0');
console.log('' == '0');
console.log(null == undefined);
console.log(null == 0);
console.log([] == false);
console.log([] == '');
console.log([19.9] == 19.9);Leia as linhas 2, 3 e 4 juntas: 0 == '' é verdadeiro, 0 == '0' é verdadeiro,
e '' == '0' é falso. A igualdade solta não é transitiva. Isso basta para
descartá-la de qualquer código novo.
As três últimas explicam por que arrays confundem tanto. [] vira a string
vazia quando comparado com um primitivo; a string vazia vira 0; false
também vira 0. Daí [] == false. E [19.9] == 19.9 funciona porque o array
de um elemento vira a string '19.9', que vira o número 19.9.
| comparação | == |
=== |
por quê |
|---|---|---|---|
'3' e 3 |
true |
false |
string vira número |
0 e '' |
true |
false |
string vazia vira 0 |
0 e '0' |
true |
false |
'0' vira 0 |
'' e '0' |
false |
false |
duas strings: compara texto |
null e undefined |
true |
false |
regra própria do == |
null e 0 |
false |
false |
null não vira número no == |
[] e false |
true |
false |
[] → '' → 0, e false → 0 |
NaN e NaN |
false |
false |
NaN não é igual a nada |
NaN não é igual a nada — nem a si mesmo
const frete = Number('doze reais');
console.log(frete);
console.log(frete === NaN);
console.log(frete == NaN);
console.log(NaN === NaN);
console.log(Number.isNaN(frete));
console.log(Object.is(frete, NaN));Quer dizer que if (frete === NaN) nunca entra, em nenhuma circunstância. Se
você já escreveu essa linha achando que estava validando um campo, ela nunca
protegeu nada. O teste correto é Number.isNaN(frete).
Object.is e o caso do zero negativo
Object.is é a terceira comparação da linguagem. Ela é igual ao ===, com duas
exceções: considera NaN igual a NaN e distingue 0 de -0.
const saldoDevedor = -0;
console.log(saldoDevedor === 0);
console.log(Object.is(saldoDevedor, 0));
console.log(Object.is(saldoDevedor, -0));
console.log(1 / saldoDevedor);O -0 parece curiosidade acadêmica até o dia em que um saldo zerado vindo de uma
subtração de centavos entra numa divisão e produz -Infinity num gráfico. Aí a
distinção deixa de ser acadêmica.
Objetos e arrays comparam por referência
Este é o ponto que mais derruba iniciante em teste automatizado. Dois objetos com o mesmo conteúdo não são iguais:
const enderecoSalvo = { cep: '01310100', numero: 200 };
const enderecoDoForm = { cep: '01310100', numero: 200 };
console.log(enderecoSalvo === enderecoDoForm);
console.log(enderecoSalvo == enderecoDoForm);
const mesmoEndereco = enderecoSalvo;
console.log(enderecoSalvo === mesmoEndereco);
console.log(JSON.stringify(enderecoSalvo) === JSON.stringify(enderecoDoForm));
console.log(['Teclado'] === ['Teclado']);O que o === compara em objeto é o endereço na memória, não o conteúdo. Trocar
para == não muda nada: quando os dois lados são objetos, a igualdade solta faz
exatamente a mesma coisa. A terceira linha é true porque mesmoEndereco e
enderecoSalvo apontam para o mesmo objeto — é uma referência copiada, não um
objeto novo.
JSON.stringify resolve o caso raso, com uma ressalva importante: ele depende da
ordem das chaves. { a: 1, b: 2 } e { b: 2, a: 1 } produzem strings
diferentes.
< e > também convertem, mas de outro jeito
console.log(249.9 > 199);
console.log('249.9' > 199);
console.log('Ácido' > 'Zebra');
console.log('Ácido'.localeCompare('Zebra', 'pt-BR'));
console.log(['Ábaco', 'Zíper', 'Bolsa'].sort());
console.log(['Ábaco', 'Zíper', 'Bolsa'].sort((a, b) => a.localeCompare(b, 'pt-BR')));Quando os dois lados são string, < e > comparam código a código, na tabela
Unicode — e Á tem código maior que Z. Por isso 'Ácido' > 'Zebra' é true e
o sort() sem argumento joga toda palavra acentuada para o fim da lista. Numa
loja brasileira isso aparece como catálogo fora de ordem. A correção é
localeCompare com o locale pt-BR, que devolve -1, 0 ou 1.
Erros comuns
if (x == false) — o bug que passa por quatro portas
A intenção é “se o cupom não veio”. O resultado é outro:
function temCupom(cupom) {
if (cupom == false) {
return 'sem cupom';
}
return `cupom aplicado: ${cupom}`;
}
console.log(temCupom(undefined));
console.log(temCupom(''));
console.log(temCupom(0));
console.log(temCupom([]));
console.log(temCupom('0'));
console.log(temCupom('PRIMEIRACOMPRA'));Leia a primeira linha de novo. O único caso que a função precisava pegar —
undefined, o cupom que não veio — é justamente o que escapou, porque
undefined == false é false. Em compensação, o teste engoliu a string vazia,
o número 0, o array vazio e a string '0', que num outro sistema poderia ser
um código de cupom legítimo.
A versão que faz o que o nome promete:
function temCupom(cupom) {
if (cupom === undefined || cupom === null || cupom === '') {
return 'sem cupom';
}
return `cupom aplicado: ${cupom}`;
}
console.log(temCupom(undefined));
console.log(temCupom(''));
console.log(temCupom(0));
console.log(temCupom('0'));
console.log(temCupom('PRIMEIRACOMPRA'));Cada condição diz exatamente o que exclui. É mais longo e é a única versão que
você consegue defender numa revisão. Quais valores o if considera falsos, sem
nenhum operador de comparação no meio, é o assunto de
truthy e falsy em JavaScript.
O erro que lança: comparar dentro de um objeto que não existe
const pedidos = [
{ id: 1042, total: 249.9 },
{ id: 1043, total: 89.9 },
];
const pedido = pedidos.find((p) => p.id === 1044);
console.log('achou?', pedido);
console.log('frete grátis?', pedido.total >= 199);TypeError: Cannot read properties of undefined (reading ‘total’) at file:///private/tmp/club-store/pedido-comparado.mjs:9:37 at ModuleJob.run (node:internal/modules/esm/module_job:439:25) at async node:internal/modules/esm/loader:633:26 at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)
Node.js v24.16.0
O find não achou nada e devolveu undefined; a comparação seguinte tentou ler
.total disso. O erro não é da comparação, é do que veio antes dela — e é o erro
mais frequente do JavaScript inteiro, detalhado em
TypeError: Cannot read properties of undefined.
switch compara com ===
Vale saber, porque não há como mudar isso:
function faixaDeFrete(regiaoId) {
switch (regiaoId) {
case 1:
return 'sudeste';
case 2:
return 'sul';
default:
return 'tabela geral';
}
}
console.log(faixaDeFrete(1));
console.log(faixaDeFrete('1'));
console.log(faixaDeFrete(Number('1')));Um switch que sempre cai no default costuma ser um id que chegou como
string de uma URL ou de um dataset do HTML. Converta antes de entrar.
Como escolher, em três linhas
===e!==por padrão. Sem exceção em código novo.x == nullsó se você quer dizer “énullouundefined” e a equipe aceita essa convenção; caso contrário, escreva as duas condições.Object.isquandoNaNou-0fazem diferença de verdade.
Na prática, quase toda comparação errada nasce um passo antes: o valor chegou
como string quando deveria ser número, ou como undefined quando deveria ser
objeto. Rever a conversão feita em
operadores aritméticos resolve mais
bugs de comparação do que decorar a tabela do ==. A próxima lição da
trilha de JavaScript combina essas comparações com
&& e ||, em operadores lógicos.
Perguntas frequentes
Existe algum caso em que o == é melhor que o ===?
Por que NaN === NaN devolve false?
Como comparar dois objetos pelo conteúdo?
Object.is substitui o ===?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — Igualdade estrita (===) — developer.mozilla.org
- MDN — Comparações de igualdade e uniformidade — developer.mozilla.org
- ECMAScript 2026 Language Specification — IsLooselyEqual — tc39.es


