Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

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

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.

Rodolfo Mori6 min de leitura

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.

js
const estoque = {
  'TEC-001': 12,
  'MOU-014': 0,
  'CAD-220': 3,
};

for (const item of estoque) {
  console.log(item);
}
file:///private/tmp/loja/objetos/percorrer-erro.mjs:7 for (const item of estoque) { ^ TypeError: estoque is not iterable at file:///private/tmp/loja/objetos/percorrer-erro.mjs:7:20 Node.js v24.16.0

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:

js
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]);
TEC-001 12 MOU-014 0 undefined function function

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

js
const estoque = {
  'TEC-001': 12,
  'MOU-014': 0,
  'CAD-220': 3,
};

for (const sku in estoque) {
  console.log(sku, '->', estoque[sku]);
}
TEC-001 -> 12 MOU-014 -> 0 CAD-220 -> 3

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:

js
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));
[ 'TEC-001', 'MOU-014', 'CAD-220' ] [ 12, 0, 3 ] [ [ 'TEC-001', 12 ], [ 'MOU-014', 0 ], [ 'CAD-220', 3 ] ]

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:

js
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);
TEC-001: 12 MOU-014: 0 (esgotado) CAD-220: 3 total de unidades: 15

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:

js
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);
0 TEC-001 12 1 MOU-014 0 2 CAD-220 3 esgotados: [ 'MOU-014' ]

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:

js
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);
}
for-in -> valor for-in -> regiao for-in -> transportadora for-in -> prazoEmDias Object.keys -> [ 'valor', 'regiao' ] própria -> valor própria -> regiao

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:

js
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));
[ '2', '10', '100', 'cliente', 'cidade' ]

As chaves que parecem inteiro não negativo saíram primeiro, em ordem numérica2, 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:

js
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);
{ 'TEC-001': 313.09, 'MOU-014': 161.89, 'CAD-220': 1294.92 } { 'TEC-001': 289.9, 'CAD-220': 1199 }

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:

js
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`);
}
sorocaba: 2 SKUs, 12 unidades campinas: 2 SKUs, 11 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:

js
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);
}
string 0 289.9 string 1 149.9 string 2 1199 number 289.9 number 149.9 number 1199

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.

Ver todos os vídeos do canal
  • objetos
  • for-in
  • object-keys
  • entries
  • iteracao

Perguntas frequentes

Por que for-of funciona em array e não em objeto?
Porque for-of exige que o valor tenha o método Symbol.iterator, e array, string, Map e Set têm. Objeto comum não tem — ele é uma coleção de propriedades, não uma sequência. Object.entries resolve porque devolve um array de pares, e array é iterável.
for-in ou Object.keys?
Object.keys na maioria dos casos: ele devolve só as chaves próprias e entrega um array, que encadeia com map, filter e reduce. for-in é útil quando você realmente quer as chaves herdadas do protótipo, o que é raro em código de aplicação e comum em código de biblioteca.
A ordem das chaves de um objeto é garantida?
É, desde o ES2015, mas não é a ordem de escrita. Chaves que parecem inteiro não negativo vêm primeiro, em ordem numérica crescente; depois as demais strings, na ordem em que foram inseridas; e por último os símbolos. Se a ordem importa para o seu caso, use um Map ou um array — não confie no literal.
Como percorrer objeto e ainda usar map ou filter?
Converta para pares com Object.entries, use os métodos de array normalmente e volte para objeto com Object.fromEntries. É o caminho de ida e volta que dá acesso ao ferramental inteiro de array sem escrever laço nenhum.

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 — Object.keys() — developer.mozilla.org
  2. MDN — for...in — developer.mozilla.org
  3. ECMAScript 2026 Language Specification — OrdinaryOwnPropertyKeys — tc39.es

Continue por aqui