Laços de Repetição: for, while e do...while

[4] Laços de Repetição: for, while e do...while

Esquecer o incremento dentro do while congela a aba, e trocar um sinal na condição faz o laço ler uma posição que não existe — sem erro nenhum, só um undefined. Entre os dois extremos: as três partes do for, o for...of que dispensa o índice, o do...while que pergunta depois, e o par break e continue.
Javascript

11 min de leitura

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 a i = 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

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.

Comentários

Mais em Javascript

Mini Projeto: Calculadora no Console
Mini Projeto: Calculadora no Console

Chegamos ao fim do primeiro módulo, e o jeito de fechá-lo é construindo: uma…

Funções: declaração, expressão e arrow functions
Funções: declaração, expressão e arrow functions

Uma função sem return devolve undefined — e uma arrow com chaves, mas sem…

NPM: gerenciando pacotes e dependências
NPM: gerenciando pacotes e dependências

Todo projeto Node começa com um package.json de quinze linhas e termina com um…