Imagine que você precisa exibir os números de 1 a 100. Você escreveria cem linhas de console.log? Claro que não. É exatamente para isso que existem os laços de repetição — eles permitem que um bloco de código seja executado várias vezes, de forma controlada e elegante.
Este é um dos conceitos mais fundamentais de qualquer linguagem de programação. Dominar os laços é o que começa a separar quem escreve código de quem resolve problemas com código.
O laço for
O for é o laço mais usado quando você sabe quantas vezes quer repetir algo.
for (let i = 1; i <= 5; i++) {
console.log(`Repetição número ${i}`);
}
// Repetição número 1
// Repetição número 2
// Repetição número 3
// Repetição número 4
// Repetição número 5
O for tem três partes separadas por ponto e vírgula:
for (inicialização ; condição ; incremento) {
// código executado a cada volta
}
- Inicialização:
let i = 1— define o ponto de partida - Condição:
i <= 5— enquanto for verdadeira, o laço continua - Incremento:
i++— executado ao final de cada volta (equivale ai = i + 1)
Contando de forma diferente
O for é flexível. Você pode contar de qualquer jeito:
// Contando de 2 em 2
for (let i = 0; i <= 10; i += 2) {
console.log(i); // 0, 2, 4, 6, 8, 10
}
// Contando de trás para frente
for (let i = 5; i >= 1; i--) {
console.log(i); // 5, 4, 3, 2, 1
}
// Tabuada do 7
for (let i = 1; i <= 10; i++) {
console.log(`7 x ${i} = ${7 * i}`);
}
Percorrendo arrays com for
Uma das aplicações mais comuns do for é percorrer listas de dados:
const frutas = ["maçã", "banana", "laranja", "uva"];
for (let i = 0; i < frutas.length; i++) {
console.log(`Fruta ${i + 1}: ${frutas[i]}`);
}
// Fruta 1: maçã
// Fruta 2: banana
// Fruta 3: laranja
// Fruta 4: uva
frutas.length retorna o tamanho do array — neste caso, 4. Por isso a condição é i < 4 (os índices vão de 0 a 3).
for...of — a forma moderna de percorrer listas
Quando você não precisa do índice, o for...of é mais limpo e legível:
const frutas = ["maçã", "banana", "laranja", "uva"];
for (const fruta of frutas) {
console.log(fruta);
}
// maçã
// banana
// laranja
// uva
Prefira o for...of sempre que só precisar dos valores. Guarde o for clássico para quando precisar controlar o índice.
O laço while
O while é usado quando você não sabe exatamente quantas vezes vai repetir — você repete enquanto uma condição for verdadeira.
let tentativas = 0;
const maxTentativas = 3;
while (tentativas < maxTentativas) {
console.log(`Tentativa ${tentativas + 1} de ${maxTentativas}`);
tentativas++;
}
// Tentativa 1 de 3
// Tentativa 2 de 3
// Tentativa 3 de 3
Cuidado com o loop infinito! Se a condição nunca se tornar false, o programa trava. Sempre garanta que algo dentro do laço vai eventualmente tornar a condição falsa.
// ⚠️ NUNCA faça isso — loop infinito:
while (true) {
console.log("Isso nunca para...");
}
Exemplo real com while — tentativas de senha
const senhaCorreta = "js2025";
let tentativa = "";
let tentativas = 0;
// Simulando tentativas
const senhasDigitadas = ["abc", "1234", "js2025", "outra"];
while (tentativa !== senhaCorreta && tentativas < senhasDigitadas.length) {
tentativa = senhasDigitadas[tentativas];
tentativas++;
if (tentativa === senhaCorreta) {
console.log(`Acesso liberado na tentativa ${tentativas}!`);
} else {
console.log(`Senha incorreta: "${tentativa}"`);
}
}
// Senha incorreta: "abc"
// Senha incorreta: "1234"
// Acesso liberado na tentativa 3!
O laço do...while
Parecido com o while, mas com uma diferença crucial: o bloco de código sempre executa pelo menos uma vez, mesmo que a condição seja falsa desde o início.
let numero = 10;
do {
console.log(`O número é ${numero}`);
numero++;
} while (numero < 5);
// O número é 10
// (executa uma vez mesmo com número >= 5)
Isso é útil, por exemplo, quando você precisa exibir um menu e pedir uma escolha antes de verificar se ela é válida:
let opcao;
// Simulando: usuário digitou opções até escolher uma válida
const entradas = [9, 0, 2];
let i = 0;
do {
opcao = entradas[i];
console.log(`Opção digitada: ${opcao}`);
i++;
} while (opcao < 1 || opcao > 3);
// Opção digitada: 9
// Opção digitada: 0
// Opção digitada: 2 ← válida, para aqui
break e continue — controlando o laço
break interrompe o laço imediatamente:
for (let i = 1; i <= 10; i++) {
if (i === 5) {
console.log("Encontrei o 5! Parando.");
break;
}
console.log(i);
}
// 1
// 2
// 3
// 4
// Encontrei o 5! Parando.
continue pula a iteração atual e vai para a próxima:
for (let i = 1; i <= 10; i++) {
if (i % 2 === 0) continue; // pula os pares
console.log(i);
}
// 1, 3, 5, 7, 9
Laços aninhados
Um laço dentro de outro. Útil para trabalhar com matrizes ou combinações:
// Tabuada completa de 1 a 3
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
console.log(`${i} x ${j} = ${i * j}`);
}
}
// 1 x 1 = 1
// 1 x 2 = 2
// 1 x 3 = 3
// 2 x 1 = 2
// ...e assim por diante
Use laços aninhados com moderação — eles crescem em complexidade rapidamente.
Comparativo rápido
| Laço | Use quando |
|---|---|
for |
Sabe o número exato de repetições |
for...of |
Quer percorrer os valores de um array |
while |
Repete enquanto uma condição for verdadeira |
do...while |
Precisa executar ao menos uma vez antes de verificar |
Tarefa para você
Tente resolver estes desafios no console do navegador:
Desafio 1: Exiba todos os números de 1 a 20 que sejam divisíveis por 3.
Desafio 2: Some todos os números de 1 a 100 e exiba o resultado.
// Dica para o desafio 2:
let soma = 0;
// seu laço aqui...
console.log(`A soma é: ${soma}`); // deve ser 5050
Desafio 3: Percorra o array abaixo e exiba apenas os nomes com mais de 4 letras.
const nomes = ["Ana", "Carlos", "Li", "Fernanda", "Bob", "Mariana"];
Ver solução — os três desafios, e por que o laço escolhido muda
// ---------------------------------------------------------------
// Desafio 1 — números de 1 a 20 divisíveis por 3
// ---------------------------------------------------------------
for (let i = 1; i <= 20; i++) {
if (i % 3 === 0) {
console.log(i); // 3, 6, 9, 12, 15, 18
}
}
// Dá para pular direto de 3 em 3 e nem testar o resto: o laço faz 6
// voltas em vez de 20. Quando a condição É o passo, ela sai do corpo.
for (let i = 3; i <= 20; i += 3) {
console.log(i);
}
// ---------------------------------------------------------------
// Desafio 2 — somar de 1 a 100
// ---------------------------------------------------------------
let soma = 0;
for (let i = 1; i <= 100; i++) {
soma += i;
}
console.log(`A soma é: ${soma}`); // A soma é: 5050
// Conferindo com a fórmula de Gauss — n * (n + 1) / 2.
// Serve de teste: se o laço estivesse errado (<= virando <, por exemplo),
// os dois números não bateriam.
const n = 100;
console.log(`Conferência: ${(n * (n + 1)) / 2}`); // 5050
// ---------------------------------------------------------------
// Desafio 3 — nomes com mais de 4 letras
// ---------------------------------------------------------------
const nomes = ["Ana", "Carlos", "Li", "Fernanda", "Bob", "Mariana"];
// for...of quando você quer o VALOR e não precisa do índice.
for (const nome of nomes) {
if (nome.length > 4) {
console.log(nome); // Carlos, Fernanda, Mariana
}
}
// A mesma coisa com filter, que já devolve o array pronto:
const longos = nomes.filter((nome) => nome.length > 4);
console.log(longos); // [ 'Carlos', 'Fernanda', 'Mariana' ]
// ---------------------------------------------------------------
// Extra — while, e o laço infinito que ele cria quando você esquece
// ---------------------------------------------------------------
let contador = 1;
while (contador <= 20) {
if (contador % 3 === 0) {
console.log(contador);
}
contador++; // sem esta linha o navegador trava: a condição nunca fica falsa
}
// ---------------------------------------------------------------
// Extra — o for...in que parece certo e não é
// ---------------------------------------------------------------
// for...in percorre CHAVES, e em array chave é string.
for (const i in nomes) {
console.log(typeof i); // "string", seis vezes
}
console.log("0" + 1); // "01" — por isso somar o índice do for...in dá texto
for...in em array entrega o índice como string: i + 1 vira "01" em vez de 1. Para array, use for...of (valor) ou for clássico (índice numérico) — for...in é para objeto.
Escolher o laço costuma se resumir a uma pergunta: você sabe de antemão quantas voltas serão? Sabendo, é for — ou for...of, quando o índice não interessa e só os valores importam. Não sabendo, é while, com o cuidado de garantir que alguma coisa dentro do corpo mude a condição, senão o navegador trava. O do...while fica para quando a primeira execução tem de acontecer de qualquer jeito, e break e continue existem porque sair no meio às vezes é mais claro do que espremer tudo na condição.
Fontes e Referências
- MDN Web Docs — for: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/for
- MDN Web Docs — while: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/while
- MDN Web Docs — do...while: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/do...while
- MDN Web Docs — for...of: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/for...of
- JavaScript.info — Loops: https://javascript.info/while-for
- Eloquent JavaScript, Cap. 2 — Marijn Haverbeke: https://eloquentjavascript.net/02_program_structure.html
- MDN — guia de laços e iteração: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Loops_and_iteration
Exercícios
Exercício 1
Trocaram um único caractere na condição do laço. Quantas linhas ele imprime agora, e o que aparece na última?
const frutas = ["maçã", "banana", "laranja", "uva"];
for (let i = 0; i <= frutas.length; i++) {
console.log(`Fruta ${i + 1}: ${frutas[i]}`);
}
Ver resposta
✓ Resposta: Imprime cinco linhas, e a última é Fruta 5: undefined. O array tem 4 elementos, então frutas.length é 4 e os índices válidos vão de 0 a 3. Com <=, o laço dá uma volta a mais e lê frutas[4], que não existe. Note que o JavaScript não reclama: ele devolve undefined e segue. A condição correta é i < frutas.length.
Exercício 2
Os dois laços abaixo têm a mesma condição e o mesmo corpo. Quantas linhas cada um imprime?
let a = 10;
while (a < 5) {
console.log(`while: ${a}`);
a++;
}
let b = 10;
do {
console.log(`do...while: ${b}`);
b++;
} while (b < 5);
Ver resposta
✓ Resposta: O while imprime nada — ele testa a condição antes de entrar, e 10 < 5 já é falso na primeira verificação. O do...while imprime uma linha, do...while: 10 — ele executa o corpo antes de testar, então roda sempre pelo menos uma vez, mesmo com a condição falsa desde o início. É toda a diferença entre os dois, e é o motivo de o do...while servir para menus: mostre a pergunta primeiro, valide a resposta depois.
Exercício 3
O laço abaixo imprime 1, 3, 5, 7, 9. O que passa a ser impresso se a condição virar i % 2 === 1? E se o continue virar break, mantendo a condição original?
for (let i = 1; i <= 10; i++) {
if (i % 2 === 0) continue;
console.log(i);
}
Ver resposta
✓ Resposta: Com i % 2 === 1, ele passa a pular os ímpares e imprime 2, 4, 6, 8, 10 — o teste seleciona quem é descartado, então inverter o teste inverte a saída. Com break no lugar do continue, imprime apenas 1: o continue pula só a volta atual e o laço segue, enquanto o break abandona o laço inteiro — e a primeira volta par é i = 2, logo depois do 1 ter sido impresso.
Exercício 4
Quantas linhas o laço aninhado abaixo imprime, e qual é exatamente a quinta delas?
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
console.log(`${i} x ${j} = ${i * j}`);
}
}
Ver resposta
✓ Resposta: São 9 linhas — o laço interno roda inteiro a cada volta do externo, então o total é 3 × 3. A quinta é 2 x 2 = 4: as três primeiras são as do i = 1, a quarta é 2 x 1 = 2 e a quinta, 2 x 2 = 4. Essa multiplicação é a razão do aviso do artigo: acrescentar um terceiro nível faria 27 voltas, e o crescimento não é somado, é multiplicado.
Exercício 5
Qual linha, se apagada, faz o laço abaixo travar o navegador? E por que o artigo recomenda um for em lugar dele?
let tentativas = 0;
const maxTentativas = 3;
while (tentativas < maxTentativas) {
console.log(`Tentativa ${tentativas + 1} de ${maxTentativas}`);
tentativas++;
}
Ver resposta
✓ Resposta: A linha tentativas++;. Sem ela, tentativas fica em 0 para sempre, a condição 0 < 3 nunca fica falsa e o laço não termina — a aba congela. Quanto à escolha do laço: aqui o número de voltas é conhecido de antemão (três), e o critério do artigo é claro — for quando se sabe quantas vezes, while quando não se sabe. O for (let i = 0; i < maxTentativas; i++) resolve o mesmo problema mantendo inicialização, condição e incremento na mesma linha, onde é difícil esquecer um deles.