Percorrer array em JavaScript: forEach, for of e for
for...of itera valores, for...in itera chaves e pega as herdadas, e forEach não aceita break nem continue. Qual laço usar em cada caso, com saída real.
Para percorrer um array em JavaScript existem três caminhos usados no dia a dia:
for clássico quando você precisa do índice e do controle total, for...of
quando você só quer os valores e pode precisar parar no meio, e forEach quando
você vai rodar um efeito para cada item e nunca vai interromper.
O quarto, for...in, existe e não é para array: ele percorre chaves, não
valores, e traz junto tudo que estiver pendurado no prototype.
A escolha não é estilo. Cada laço abre mão de uma coisa: forEach abre mão de
break, continue e do return que você acha que está escrevendo; for...in
abre mão da garantia de que só os itens vão aparecer. Esta lição mostra cada
perda com o código rodando.
Três formas de percorrer a mesma prateleira
Imagine conferir uma prateleira. Você pode andar com uma planilha de posições, olhar cada produto diretamente ou pedir a outra pessoa que execute a mesma tarefa em todos eles. Os três caminhos chegam aos itens, mas só alguns deixam voltar, pular ou interromper a conferência no meio.
No JavaScript, esses caminhos são for, for...of e forEach. O primeiro
expõe o índice e todo o controle; o segundo entrega valores e aceita break; o
terceiro chama um callback para cada item e não pode ser interrompido dessa
forma. No primeiro exemplo, anote índice e valor de cada volta antes de conferir
a saída — essa tabela pequena revela qual ferramenta entrega o controle que a
tarefa pede.
const carrinho = ['Teclado mecânico', 'Mouse sem fio', 'Headset'];
for (let i = 0; i < carrinho.length; i++) {
console.log(`${i + 1}. ${carrinho[i]}`);
}Verboso, e ainda assim insubstituível em três situações: quando você precisa andar de dois em dois, quando precisa percorrer de trás para a frente, e quando vai remover itens durante o percurso — os três casos em que o índice é o assunto, não um detalhe.
Note o let i, não var i. Com var, todas as voltas compartilham a mesma
variável, e qualquer função criada dentro do laço enxerga o valor final. É o
clássico que a lição de variáveis em JavaScript
reproduz no terminal.
for...of: o laço padrão de hoje
const pedidos = [
{ numero: 1041, total: 289.9 },
{ numero: 1042, total: 1199 },
{ numero: 1043, total: 79.5 },
];
for (const pedido of pedidos) {
if (pedido.total < 100) continue;
if (pedido.total > 1000) {
console.log(`Pedido ${pedido.numero} vai para análise manual`);
break;
}
console.log(`Pedido ${pedido.numero} liberado: R$ ${pedido.total.toFixed(2)}`);
}Repare no que não foi impresso: o pedido 1043 nunca chegou a ser avaliado,
porque o break encerrou o laço. Esse é o poder que for...of tem e forEach
não tem.
for...of funciona em qualquer coisa iterável, não só em array: String,
Set, Map, NodeList, arguments, o retorno de .entries(). É a razão de
ele ter virado o laço padrão.
const skus = new Set(['TEC-001', 'MOU-014', 'TEC-001']);
const precos = new Map([
['TEC-001', 289.9],
['MOU-014', 149.9],
]);
for (const sku of skus) {
console.log(sku, precos.get(sku));
}
for (const [sku, preco] of precos) {
console.log(`${sku}: R$ ${preco.toFixed(2)}`);
}O Set derrubou o SKU repetido sozinho, e o Map entregou par a par, pronto
para desestruturar. O mesmo for...of serviu para os dois.
forEach: um efeito por item, e nada de sair no meio
forEach recebe uma função e a chama para cada item. Ela ganha três
argumentos: o item, o índice e o array inteiro.
const carrinho = [
{ nome: 'Teclado mecânico', preco: 289.9 },
{ nome: 'Mouse sem fio', preco: 149.9 },
{ nome: 'Headset', preco: 399 },
];
carrinho.forEach((item, indice, lista) => {
const ultimo = indice === lista.length - 1 ? ' (último)' : '';
console.log(`${indice}: ${item.nome} — R$ ${item.preco.toFixed(2)}${ultimo}`);
});Lê bem, e para renderizar uma lista é o que a maioria escreve. O preço vem agora.
O return dentro do forEach não é o que você pensa
Como o corpo do laço é uma função, o return
pertence a ela, não a quem chamou o forEach. E o forEach descarta esse
valor:
const precos = [289.9, 149.9, 399, 79.5];
function primeiroCaro(lista) {
lista.forEach((preco) => {
if (preco > 200) {
return preco;
}
});
return 'nenhum';
}
console.log(primeiroCaro(precos));
let visitados = 0;
precos.forEach((preco) => {
visitados += 1;
if (preco > 200) return;
console.log('barato:', preco);
});
console.log('itens visitados:', visitados);Duas lições numa saída só. A primeira: primeiroCaro devolveu 'nenhum'
mesmo tendo três itens acima de 200 — o return preco saiu do callback e
ninguém pegou o valor. A segunda: o return do segundo bloco funcionou como
continue, pulando a impressão, mas os quatro itens foram visitados. Ele
não é break.
E break nem chega a rodar: break é sintaxe de laço, e ali dentro não existe
laço nenhum, só uma chamada de função.
for...in percorre chaves — inclusive as herdadas
const carrinho = ['Teclado mecânico', 'Mouse sem fio', 'Headset'];
Array.prototype.freteGratis = true;
for (const chave in carrinho) {
console.log(typeof chave, JSON.stringify(chave), carrinho[chave]);
}Duas coisas quebradas de uma vez.
A primeira está na coluna do typeof: as chaves saem como string. Faça
chave + 1 esperando o próximo índice e você recebe "01", não 1.
A segunda é a quarta linha. Alguém — uma biblioteca antiga, um polyfill mal
escrito, você mesmo em outro arquivo — acrescentou uma propriedade ao
Array.prototype, e ela entrou no laço como se fosse item do carrinho. O
for...in não pergunta se a chave é do objeto ou de um ancestral dele.
Um array esparso torna isso pior ainda: for...in pula os buracos, então o
número de voltas nem bate com o length.
for...in é a ferramenta certa para percorrer as chaves de um objeto. Em
array, use for...of.
Índice e valor juntos: entries()
Se você escolheu for...of mas precisa do índice, não volte para o for
clássico:
const carrinho = ['Teclado mecânico', 'Mouse sem fio', 'Headset'];
for (const [indice, nome] of carrinho.entries()) {
console.log(`${indice + 1}º item do pedido: ${nome}`);
}
console.log([...carrinho.keys()]);entries() devolve pares [índice, valor], e a desestruturação separa os dois
na própria linha do for. A última saída é a prova do que importa aqui:
keys() mostra os índices espalhados como 0, 1, 2 — números, sem
aspas. Diferente do for...in, indice + 1 soma em vez de concatenar.
Remover enquanto percorre pula item
const carrinho = ['Teclado mecânico', 'Mouse sem fio', 'Headset', 'Webcam'];
for (const [indice, nome] of carrinho.entries()) {
if (nome === 'Headset') {
carrinho.splice(indice, 1);
}
console.log(indice, nome);
}
console.log(carrinho);O array tinha quatro itens e o laço rodou três vezes. 'Webcam' nunca foi
impressa: quando o Headset saiu da posição 2, a Webcam deslizou para lá, o
laço avançou para a posição 3 e não achou mais nada.
Duas saídas, ambas melhores que “tomar cuidado”: percorra de trás para a frente
com o for clássico, ou construa um array novo com filter. Se você vai
remover mesmo, a lição
adicionar e remover elementos de um array
tem o inventário completo dos métodos que mutam.
Parar cedo sem escrever laço: some, find, every
Muita busca escrita com laço na mão já tem um método que faz o mesmo e para sozinho no primeiro acerto:
const carrinho = [
{ nome: 'Teclado mecânico', estoque: 4 },
{ nome: 'Mouse sem fio', estoque: 0 },
{ nome: 'Headset', estoque: 9 },
];
let consultas = 0;
const semEstoque = carrinho.some((item) => {
consultas += 1;
return item.estoque === 0;
});
console.log(semEstoque, 'depois de', consultas, 'consultas');
console.log(carrinho.every((item) => item.estoque >= 0));
console.log(carrinho.find((item) => item.estoque === 0)?.nome);
console.log(carrinho.findIndex((item) => item.estoque === 0));O contador é a prova: some parou na segunda consulta, sem tocar no Headset.
every faz o simétrico — para no primeiro item que reprova. find devolve o
item, findIndex devolve a posição, e ambos param também.
O erro: forEach is not a function
Este erro aparece na primeira vez que alguém tenta percorrer elementos da
página. Reproduzido aqui com um objeto que tem exatamente o formato de uma
HTMLCollection — chaves numéricas e length, sem herdar de Array:
const elementos = { 0: 'Teclado mecânico', 1: 'Mouse sem fio', length: 2 };
elementos.forEach((nome) => console.log(nome));No navegador a mensagem é idêntica, sem o caminho do arquivo. E a causa é
sempre a mesma: document.getElementsByClassName('item') e
document.getElementsByTagName('li') devolvem uma HTMLCollection, que se
parece com array — tem length, tem colecao[0] — mas não herda de
Array.prototype. Nada de forEach, map ou filter.
O detalhe que confunde: document.querySelectorAll('.item') devolve uma
NodeList, e essa tem forEach. Não tem map nem filter. Por isso o
mesmo código funciona com um seletor e falha com o outro.
A correção é converter para array de verdade antes:
const elementos = { 0: 'Teclado mecânico', 1: 'Mouse sem fio', length: 2 };
const lista = Array.from(elementos);
console.log(Array.isArray(elementos), Array.isArray(lista));
lista.forEach((nome, i) => console.log(i, nome));
for (const nome of lista) {
console.log('for...of:', nome);
}Array.from(colecao) funciona com qualquer coisa que tenha length ou seja
iterável. O spread, [...colecao], é mais curto e só funciona com iteráveis —
o que cobre NodeList e HTMLCollection, mas não um objeto array-like puro
como o deste exemplo.
Qual laço usar
| situação | laço | por quê |
|---|---|---|
| só os valores, sem parar | for...of |
mais legível, funciona em iterável |
precisa de break |
for...of |
forEach não tem |
| precisa do índice | for...of + entries() |
índice é número, não string |
| efeito por item, lista curta | forEach |
expressa a intenção |
| remover durante o percurso | for clássico, de trás para a frente |
evita o item pulado |
| existe algum que…? | some / find |
param no primeiro acerto |
| chaves de um objeto | for...in |
é o caso de uso real dele |
| transformar a lista | map / filter |
laço nenhum |
Repare na última linha: uma boa parte dos laços escritos por iniciante não
deveria ser laço. Percorrer para montar outro array é map; percorrer para
selecionar é filter; percorrer somando é reduce. É o assunto da próxima
lição da trilha de JavaScript,
map, filter e reduce.
Antes dela, se carrinho[i] estiver devolvendo undefined no meio do laço,
volte para arrays em JavaScript — o
length e os buracos explicam a maioria dos casos. O
guia completo de JavaScript mostra a trilha inteira.
Prefere aprender em vídeo?
Tem aula sobre este assunto no nosso canal.
DevClub no YouTubeAPRENDA JavaScript For | For Of | For In - Simples e Direto ao PontoAssistir a aula
DevClub no YouTubeAprenda JavaScript forEach de forma RÁPIDA e SIMPLESAssistir a aula
Perguntas frequentes
Posso usar break dentro de forEach?
Por que for...in é desaconselhado em array?
forEach é mais lento que o for clássico?
Posso remover itens do array enquanto percorro?
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 — Loops and iteration — developer.mozilla.org
- MDN — Array.prototype.forEach() — developer.mozilla.org
- MDN — HTMLCollection — developer.mozilla.org


