Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

== 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).

Rodolfo Mori6 min de leitura

=== 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.

js
const quantidadeDigitada = '3';
const quantidadeEmEstoque = 3;

console.log(quantidadeDigitada === quantidadeEmEstoque);
console.log(Number(quantidadeDigitada) === quantidadeEmEstoque);
console.log('PRIMEIRACOMPRA' === 'PRIMEIRACOMPRA');
console.log(199 !== 199.0);
false true true false

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.

js
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);
true true true false true false true true true

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 false0
NaN e NaN false false NaN não é igual a nada

NaN não é igual a nada — nem a si mesmo

js
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));
NaN false false false true true

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.

js
const saldoDevedor = -0;

console.log(saldoDevedor === 0);
console.log(Object.is(saldoDevedor, 0));
console.log(Object.is(saldoDevedor, -0));
console.log(1 / saldoDevedor);
true false true -Infinity

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:

js
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']);
false false true true false

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

js
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')));
true true true -1 [ 'Bolsa', 'Zíper', 'Ábaco' ] [ 'Ábaco', 'Bolsa', 'Zíper' ]

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:

js
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'));
cupom aplicado: undefined sem cupom sem cupom sem cupom sem cupom cupom aplicado: 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:

js
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'));
sem cupom sem cupom cupom aplicado: 0 cupom aplicado: 0 cupom aplicado: 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

js
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);
achou? undefined file:///private/tmp/club-store/pedido-comparado.mjs:9 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:

js
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')));
sudeste tabela geral sudeste

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 == null só se você quer dizer “é null ou undefined” e a equipe aceita essa convenção; caso contrário, escreva as duas condições.
  • Object.is quando NaN ou -0 fazem 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.

  • operadores
  • comparacao
  • igualdade
  • coercao
  • nan

Perguntas frequentes

Existe algum caso em que o == é melhor que o ===?
Um só, e é uma convenção, não uma necessidade — x == null é verdadeiro para null e para undefined, e falso para todo o resto. Serve como atalho para "não tem valor". Fora disso, todo == que sobrevive numa revisão de código é um === esperando para acontecer.
Por que NaN === NaN devolve false?
Porque NaN representa "o resultado desta conta não é um número", e dois cálculos que falharam por motivos diferentes não deveriam ser considerados o mesmo valor. A especificação do IEEE 754 define isso, e o JavaScript apenas obedece. Para testar, use Number.isNaN ou Object.is.
Como comparar dois objetos pelo conteúdo?
Não existe operador para isso. Para objetos rasos e com a mesma ordem de chaves, JSON.stringify dos dois resolve. Para estrutura aninhada ou ordem diferente, escreva uma função que compare campo a campo, ou use o isEqual de uma biblioteca. O === sempre vai comparar o endereço na memória.
Object.is substitui o ===?
Quase. Object.is se comporta como === em tudo, menos em dois pontos: ele considera NaN igual a NaN, e distingue 0 de -0. É a comparação certa quando esses dois casos importam — em código de gráfico, física ou matemática financeira com saldo negativo zerado.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

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

  1. MDN — Igualdade estrita (===) — developer.mozilla.org
  2. MDN — Comparações de igualdade e uniformidade — developer.mozilla.org
  3. ECMAScript 2026 Language Specification — IsLooselyEqual — tc39.es

Continue por aqui