Percorrer objeto em JavaScript: Object.keys e for in
Objeto não é iterável e quebra no for-of. Veja for-in, Object.keys, values e entries, hasOwn e a ordem real das chaves, com saída no Node 24.16.0.
Percorrer um objeto é a operação que quase todo iniciante tenta fazer com
for...of — e que falha na primeira execução, porque objeto comum não é
iterável em JavaScript. Array é, string é, Map e Set são. Objeto literal
não.
A resposta curta: use for...in quando quiser as chaves direto, e
Object.keys, Object.values ou Object.entries quando quiser trabalhar com
elas como array — que é a opção que dá acesso a map, filter e reduce.
Os exemplos usam o estoque de uma loja, indexado por SKU: um objeto em que cada chave é um código de produto e cada valor é a quantidade disponível.
Conferir uma ficha campo por campo
Uma ficha de estoque tem rótulos e respostas: SKU, quantidade, corredor. Você não a percorre como uma fila de caixas; primeiro lê o nome do campo e depois o valor que está ao lado. Um objeto comum segue essa lógica de ficha, não a lógica de sequência de um array.
Por isso ele não implementa sozinho o protocolo iterável usado pelo
for...of. for...in fornece chaves; Object.keys, values e entries
transformam partes da ficha em arrays iteráveis. Na primeira execução, preveja
se cada volta imprime rótulo, valor ou os dois e confira a forma exata na saída.
const estoque = {
'TEC-001': 12,
'MOU-014': 0,
'CAD-220': 3,
};
for (const item of estoque) {
console.log(item);
}A mensagem é literal. for...of procura no valor um método chamado
Symbol.iterator, que descreve como produzir os elementos um a um. Array tem
esse método; objeto literal não tem, porque ele é uma coleção de propriedades
com nome, não uma sequência com ordem de percurso.
Dá para conferir isso na prática, e de quebra ver como converter:
const estoque = { 'TEC-001': 12, 'MOU-014': 0 };
const estoqueMap = new Map(Object.entries(estoque));
for (const [sku, quantidade] of estoqueMap) {
console.log(sku, quantidade);
}
console.log(typeof estoque[Symbol.iterator]);
console.log(typeof estoqueMap[Symbol.iterator]);
console.log(typeof Object.entries(estoque)[Symbol.iterator]);Map foi feito para esse uso: ele guarda pares chave-valor, é iterável por
natureza e aceita qualquer tipo como chave, inclusive objeto e número — sem a
conversão para string que o objeto comum faz.
for...in: o laço que existe para objeto
const estoque = {
'TEC-001': 12,
'MOU-014': 0,
'CAD-220': 3,
};
for (const sku in estoque) {
console.log(sku, '->', estoque[sku]);
}A variável do for...in recebe a chave, sempre como string, e o valor você
busca com colchetes — a notação que a lição de
objetos em JavaScript detalha. É uma
letra de diferença para o for...of, e ela muda tudo: of percorre valores de
coisas iteráveis, in percorre nomes de propriedade.
Object.keys, values e entries
Os três transformam o objeto em array, e é aí que o ferramental de array fica disponível:
const estoque = {
'TEC-001': 12,
'MOU-014': 0,
'CAD-220': 3,
};
console.log(Object.keys(estoque));
console.log(Object.values(estoque));
console.log(Object.entries(estoque));entries devolve um array de pares — cada par é um array de dois elementos,
chave e valor. É o formato que combina com desestruturação no for...of:
const estoque = {
'TEC-001': 12,
'MOU-014': 0,
'CAD-220': 3,
};
for (const [sku, quantidade] of Object.entries(estoque)) {
const alerta = quantidade === 0 ? ' (esgotado)' : '';
console.log(`${sku}: ${quantidade}${alerta}`);
}
const totalEmEstoque = Object.values(estoque).reduce((s, q) => s + q, 0);
console.log('total de unidades:', totalEmEstoque);Esse é o padrão que resolve a maioria dos casos reais: Object.entries no
for...of, com [chave, valor] já separados. Quando só os valores interessam,
Object.values mais reduce faz a conta numa linha.
forEach também está disponível, e traz o índice de brinde:
const estoque = { 'TEC-001': 12, 'MOU-014': 0, 'CAD-220': 3 };
Object.entries(estoque).forEach(([sku, quantidade], indice) => {
console.log(indice, sku, quantidade);
});
const esgotados = Object.keys(estoque).filter((sku) => estoque[sku] === 0);
console.log('esgotados:', esgotados);Chaves herdadas e Object.hasOwn
Aqui está a diferença de comportamento que separa for...in de Object.keys,
e o motivo de tanto código antigo ter um if estranho dentro do laço:
const padraoDeFrete = { transportadora: 'Correios', prazoEmDias: 6 };
const freteDoPedido = Object.create(padraoDeFrete);
freteDoPedido.valor = 27.4;
freteDoPedido.regiao = 'nordeste';
for (const chave in freteDoPedido) {
console.log('for-in ->', chave);
}
console.log('Object.keys ->', Object.keys(freteDoPedido));
for (const chave in freteDoPedido) {
if (Object.hasOwn(freteDoPedido, chave)) console.log('própria ->', chave);
}O for...in subiu na cadeia de protótipos e trouxe transportadora e
prazoEmDias, que pertencem ao objeto padrão. Object.keys ficou só nas duas
propriedades próprias. Em objeto literal simples os dois dão o mesmo resultado
— a diferença aparece com Object.create, com instâncias de classe e com
objetos vindos de bibliotecas.
A ordem das chaves não é a ordem que você escreveu
Esta é a parte que surpreende quem monta relatório:
const relatorio = {
cliente: 'Ana Souza',
100: 'faixa até R$ 100',
10: 'faixa até R$ 10',
cidade: 'Sorocaba',
2: 'faixa até R$ 2',
};
console.log(Object.keys(relatorio));As chaves que parecem inteiro não negativo saíram primeiro, em ordem
numérica — 2, 10, 100 —, e só depois vieram as strings comuns, na ordem
em que foram escritas. Não é aleatório e não muda entre execuções: é a regra do
padrão, aplicada por Object.keys, Object.values, Object.entries,
for...in e JSON.stringify.
A consequência prática: um objeto com chave numérica não preserva a ordem
de inserção. Se a ordem faz parte da informação — faixas de faturamento, etapas
de um funil, meses do ano —, use um array de objetos ou um Map, que respeita
a ordem de inserção sempre.
Transformar o objeto e voltar
Object.fromEntries é o caminho de volta: recebe a lista de pares e monta o
objeto de novo. Com ele, map e filter passam a funcionar sobre objetos:
const precos = {
'TEC-001': 289.9,
'MOU-014': 149.9,
'CAD-220': 1199,
};
const comReajuste = Object.fromEntries(
Object.entries(precos).map(([sku, preco]) => [sku, Number((preco * 1.08).toFixed(2))]),
);
console.log(comReajuste);
const acimaDe200 = Object.fromEntries(
Object.entries(precos).filter(([, preco]) => preco > 200),
);
console.log(acimaDe200);Repare na vírgula sozinha em ([, preco]): é a desestruturação ignorando o
primeiro elemento do par. O callback é uma arrow comum, do tipo que a lição de
funções em JavaScript descreve.
Objeto dentro de objeto
Estoque por loja é um objeto de objetos, e o percurso simplesmente se aninha:
const estoquePorLoja = {
sorocaba: { 'TEC-001': 12, 'MOU-014': 0 },
campinas: { 'TEC-001': 4, 'CAD-220': 7 },
};
for (const [loja, itens] of Object.entries(estoquePorLoja)) {
const total = Object.values(itens).reduce((s, q) => s + q, 0);
console.log(`${loja}: ${Object.keys(itens).length} SKUs, ${total} unidades`);
}O laço externo entrega a loja e o objeto de itens; o interno vira uma redução, sem precisar de segundo laço. Quando a estrutura tem três ou mais níveis, vale extrair a parte interna para uma função com nome — e, se os dados vierem como lista em vez de objeto indexado, as receitas estão em array de objetos.
for...in em array: não faça
for...in funciona em array, porque array também é objeto. O resultado é
sutilmente errado:
const precos = [289.9, 149.9, 1199];
for (const i in precos) {
console.log(typeof i, i, precos[i]);
}
for (const preco of precos) {
console.log(typeof preco, preco);
}O índice veio como string, o que quebra qualquer conta feita com ele —
i + 1 produz "01" em vez de 1. Além disso, for...in também percorreria
propriedades avulsas coladas no array e pula as posições vazias. Em array, use
for...of para valores, forEach quando precisar do índice e entries()
quando quiser os dois.
Qual usar em cada caso
| você quer | use | observação |
|---|---|---|
| as chaves, direto | for...in |
inclui as herdadas do protótipo |
| as chaves como array | Object.keys |
só as próprias; encadeia com filter |
| só os valores | Object.values |
ideal para somar e contar |
| chave e valor juntos | Object.entries |
com for...of e desestruturação |
| transformar e voltar a objeto | entries + map + fromEntries |
ida e volta em uma expressão |
| ordem de inserção garantida | Map |
e chave que não precisa ser string |
A regra que evita quase todo problema: comece por Object.entries no
for...of. Ele cobre a maior parte dos casos, deixa chave e valor explícitos no
código e não traz nada herdado sem você pedir. Guarde o for...in para quando
a herança de protótipo for exatamente o que você está investigando.
Com objetos e arrays dominados, a próxima lição da trilha de JavaScript mostra como extrair essas chaves em variáveis de uma vez só, em desestruturação, spread e rest. Se quiser revisar a base antes, comece por objetos em JavaScript; o guia completo de JavaScript mostra o mapa inteiro e tudo o que já publicamos sobre JavaScript fica reunido na página da tecnologia.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Por que for-of funciona em array e não em objeto?
for-in ou Object.keys?
A ordem das chaves de um objeto é garantida?
Como percorrer objeto e ainda usar map ou filter?
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 — Object.keys() — developer.mozilla.org
- MDN — for...in — developer.mozilla.org
- ECMAScript 2026 Language Specification — OrdinaryOwnPropertyKeys — tc39.es



