Dia 6 — Lacos de repeticao
for, while e do...while
for, while e do...while: repetir um bloco
for repete um bloco um número controlado de vezes: inicializa o contador, testa a condição e atualiza. As três partes ficam separadas por ; dentro dos parênteses, e a ordem importa — o teste vem antes do corpo, então um limite igual ao valor inicial não entra nenhuma vez.
Em inglês essa estrutura é o loop, e o nome é o que o aluno costuma digitar na busca. O termo técnico em português é laço, e é a palavra que aparece no resto desta página.
for (let i = 0; i < 3; i++) { console.log('volta', i); } // volta 0 / volta 1 / volta 2 — e o i depois do for vale 3
O let do cabeçalho é restrito ao laço: fora dele a variável não existe (dia 2). O i++ é o que impede o laço de ser infinito; sem ele, a condição de laco nunca muda e o programa trava. O que encerra o laço é a condição de parada: o teste que deixa de ser verdadeiro.
while e do...while
while testa a condição antes de cada volta, como o for. do...while testa depois, então o corpo roda pelo menos uma vez — é a forma de pedir menu, confirmação e leitura, onde zero volta não faz sentido.
let n = 5; while (n > 0) { n -= 2; // 5, 3, 1 — para em -1 } let tentativa = 0; do { tentativa++; } while (tentativa < 3);
O par break e continue existe nos três. break sai do laço na hora; continue pula para a próxima volta, sem rodar o que está abaixo. continue num for dispara o incremento; num while não dispara nada, e é por isso que o while com continue e sem n++ é laço infinito.
Escolher entre os três
| Estrutura | Assinatura | Use quando |
|---|---|---|
for | for (inicio; condicao; incremento) { } | você já sabe quantas vezes |
for...of | for (item de iteravel) { } | você vai percorrer uma lista (dia 6, aula 2) |
while | while (condicao) { } | quantas vezes depende do que acontece dentro |
do...while | do { } while (condicao); | o corpo tem que rodar pelo menos uma vez |
O incremento não tem que ser i++. Qualquer expressão que valha um passo serve, e o i-- é a forma de contar ao contrário — o contador que não é para ser lido em ordem. O i += 2 conta de dois em dois, para os índices pares.
for (let i = 3; i > 0; i--) { } // 3, 2, 1 — o i-- anda para tras for (let i = 0; i < 6; i += 2) { } // 0, 2, 4 — de dois em dois
Cada volta do laço é uma iteracao. É o termo que o material usa para o corpo inteiro do for ou do while, e o mesmo que aparece em entries() na aula 2.
Com a regra do "duas vezes ou mais, use laço", um for escrito à mão de 0 até lista.length quase sempre deveria ser for...of ou forEach. O for com índice fica quando o índice é o dado — matriz, índice menor/maior, dois laços aninhados.
Laço infinito e saída antecipada
Laço sem condição é while (true) {}; a saída vem de dentro, por break ou por return. É a forma de laço mais comum em código real, porque a condição real costuma ser "deu erro" ou "achou o que procurava", não uma contagem.
function procura(lista) { for (const item of lista) { if (item === 'alvo') { return 'achei na volta ' + lista.indexOf(item); } } return 'nao achei'; }
for (;;)éforsem nenhuma das três partes: teste nunca acontece, laço infinito. E umforque só altera a variável de teste, nunca a do incremento, também é infinito.NaNé o outro fim sem saída:i = i + 1comicomeçando emNaNnunca chega em nenhuma condição, porque nenhuma comparação comNaNé verdadeira.
Exemplo
'use strict'; // Exemplo: os tres lacos de repeticao, break e continue. // for: inicializa, testa, incrementa. O limite e exclusive. for (let i = 0; i < 3; i++) { console.log('for:', i); } // O let do cabecalho nao existe depois do for. console.log('i nao existe aqui:', typeof i); // while: a condicao e testada ANTES de cada volta. let n = 5; while (n > 0) { n -= 2; console.log('while:', n); } // do...while: testa DEPOIS, entao o corpo roda pelo menos uma vez. let tentativa = 0; do { tentativa++; console.log('do...while: tentativa', tentativa); } while (tentativa < 3); // Incremento maior que 1 nao precisa de for: while resolve. let linha = 0; while (linha < 6) { linha += 2; } console.log('linhas pares de 0 a 5:', linha); // break sai do laco na hora. let somatorio = 0; for (const numero of [4, 8, 15, 16, 23, 42]) { if (numero > 20) { console.log('break no numero', numero, '| acumulado ate aqui:', somatorio); break; } somatorio += numero; } console.log('somatorio final:', somatorio); // continue pula o resto do corpo e vai para a proxima volta. console.log('--- so os numeros impares ---'); for (let i = 1; i <= 10; i++) { if (i % 2 === 0) { continue; } console.log(i); } // continue dentro de um for ainda dispara o incremento do cabecalho. console.log('--- i no continue ---'); for (let i = 0; i < 3; i++) { if (i === 1) { continue; } console.log('i =', i); } // Return antecipado: sai da funcao, nao so do laco. function somaAte(lista, limite) { let total = 0; for (const valor of lista) { if (valor > limite) { return total; } total += valor; } return total; } console.log('somaAte([1,2,3,4,5], 3) =', somaAte([1, 2, 3, 4, 5], 3)); // while (true): a condicao real costuma ser "deu erro" ou "achou". function validade(opcoes) { let i = 0; while (true) { if (i >= opcoes.length) { return 'opcao invalida'; } if (opcoes[i] === 'sim') { return 'confirmado na opcao ' + i; } i++; } } console.log(validade(['nao', 'talvez', 'sim']), '|', validade(['nao', 'nao'])); // while com continue e sem incremento e laco infinito: e por isso que // o for e mais seguro quando o continue fica no corpo. let seguro = 0; for (let i = 0; i < 3; i++) { if (i === 0) { continue; } seguro += 1; } console.log('o for disparou o incremento mesmo com continue:', seguro);
Saída real
for: 0 for: 1 for: 2 i nao existe aqui: undefined while: 3 while: 1 while: -1 do...while: tentativa 1 do...while: tentativa 2 do...while: tentativa 3 linhas pares de 0 a 5: 6 break no numero 23 | acumulado ate aqui: 43 somatorio final: 43 --- so os numeros impares --- 1 3 5 7 9 --- i no continue --- i = 0 i = 2 somaAte([1,2,3,4,5], 3) = 6 confirmado na opcao 2 | opcao invalida o for disparou o incremento mesmo com continue: 2
for...of e for...in: percorrer colecoes
Percorrer lista: for...of e for...in
for...of percorre os valores. for...in percorre as chaves. É a diferença inteira, e escolher o errado é a causa número um de bug em laço: for...in num array devolve os índices em forma de texto, e o código continua rodando sem erro nenhum.
const frutas = ['maçã', 'banana', 'uva']; for (const fruta of frutas) { // valores: 'maçã', 'banana', 'uva' console.log(fruta); } for (const indice in frutas) { // chaves: '0', '1', '2' (texto) console.log(indice, frutas[indice]); }
Regra prática: for...in é para objeto, e objeto é dia 9. Num array, use for...of.
É o for...of que percorre array: cada volta entrega um elemento, na ordem em que ele está, sem passar índice. Percorrer array é, no fundo, o que o map e o filter fazem por baixo — o for...of entrega o elemento, o método decide o que fazer com ele.
O que dá para percorrer
for...of aceita qualquer iterável: array, string (cada caractere, um por vez), Set, Map (cada par [chave, valor]), NodeList do DOM e o arguments implícito. for...in aceita qualquer objeto com propriedades enumeráveis, inclusive um array.
| Forma | Assinatura | Entrega |
|---|---|---|
for...of | for (const item of iteravel) { } | o valor de cada elemento |
for...in | for (const chave in objeto) { } | o nome da chave, sempre texto |
entries() | iteravel.entries() | pares [indice, valor], para for...of |
keys() | iteravel.keys() | só os índices, para for...of |
values() | iteravel.values() | só os valores, é o padrão do for...of |
Quem precisa do índice e do valor usa entries(): for (const [i, valor] of lista.entries()). A desestruturação vem no dia 9; por enquanto o par também funciona com par[0] e par[1].
Percorrer string e quebrando no meio
O for...of sobre texto devolve caracteres, com acento e emoji contados como um só:
for (const letra of 'Sala 12 -ção') { }
break e continue valem igual dentro dos dois. O break dentro do laço encerra o laço inteiro, na hora, e o que está abaixo dele não roda; o continue no laço pula para a próxima volta, deixando o resto do corpo para trás. Um for dentro de outro com break só sai o de dentro — para sair os dois de uma vez, o return resolve.
Quando o for...of não serve
for...of não dá índice numérico para usar como posição. lista.indexOf('uva') devolve 2; o i do for...of não existe, e o indice do for...in é '2', texto — com ele, lista[indice] funciona, mas indice + 1 concatena em vez de somar.
Nos três casos abaixo, volte para o for com índice:
- Precisa do índice para calcular, não só para exibir.
- Precisa andar em duas listas ao mesmo tempo no mesmo índice.
- A lista muda de tamanho dentro do laço e o fim deve ser fixado no começo:
for (let i = 0; i < lista.length; i++) { }
Percorrer o resultado de um
filtercomfor...indevolve os índices originais do array, não os do array novo — é por isso que omapsobre umfor...inembaralha a posição. E umfor...ofsobre uma string quebra o texto em caracteres, nunca em palavras: para isso ésplit(' ')(dia 4).
Exemplo
'use strict'; // Exemplo: for...of percorre valores, for...in percorre chaves. const frutas = ['maçã', 'banana', 'uva']; // for...of entrega cada valor, na ordem. console.log('--- for...of (valores) ---'); for (const fruta of frutas) { console.log(fruta); } // for...in entrega o NOME da chave. Em array, a chave e o indice em texto. console.log('--- for...in (chaves) ---'); for (const indice in frutas) { console.log(JSON.stringify(indice), '->', frutas[indice], '| typeof', typeof indice); } // A prova do bug: indice e texto, entao a conta vira concatenacao. for (const indice in frutas) { console.log('indice + 1 =', indice + 1, '(texto); Number(indice) + 1 =', Number(indice) + 1); } // for...of nao tem indice. Quem precisa dele usa entries(). console.log('--- for...of com entries() ---'); for (const [indice, valor] of frutas.entries()) { console.log(indice, valor); } // string e iteravel: um caractere por volta, acento junto. console.log('--- string ---'); for (const caractere of 'ação') { console.log(caractere, '-', caractere.length, 'unidade de codigo'); } // Set nao repete valor: percorrer duas vezes a mesma chave nao acontece. const idiomas = new Set(['pt', 'en', 'pt', 'es']); console.log('--- Set, sem repeticao ---'); for (const idioma of idiomas) { console.log(idioma); } // Map entrega o par [chave, valor] no for...of. const estoque = new Map([['arroz', 3], ['feijão', 7]]); console.log('--- Map ---'); for (const [produto, quantidade] of estoque) { console.log(produto, '=', quantidade); } // break e continue funcionam igual nos dois. console.log('--- break ---'); for (const fruta of frutas) { if (fruta === 'uva') { break; } console.log('ate parar:', fruta); } console.log('--- continue ---'); for (const fruta of frutas) { if (fruta === 'banana') { continue; } console.log('passei:', fruta); } // Percorrer uma lista de objetos, que e o caso real de uso. const pedidos = [ { id: 1, cliente: 'Ana' }, { id: 2, cliente: 'Bia' }, { id: 3, cliente: 'Cau' }, ]; console.log('--- pedidos ---'); for (const pedido of pedidos) { console.log(pedido.id, pedido.cliente); } // Duas listas andando juntas: precisa de indice numerico. const nomes = ['Ana', 'Bia', 'Cau']; const notas = [8, 9, 7]; console.log('--- duas listas no mesmo indice ---'); for (let i = 0; i < nomes.length; i++) { console.log(nomes[i], '=', notas[i]); } // for...of tambem funciona em qualquer objeto com Symbol.iterator. // E por isso que NodeList, Set e Map respondem ao for...of sem adapter. const Iteravel = { itens: [10, 20], *[Symbol.iterator]() { yield* this.itens; }, }; console.log('--- objeto com Symbol.iterator ---'); let total = 0; for (const item of Iteravel) { total += item; } console.log('soma do iteravel:', total);
Saída real
--- for...of (valores) --- maçã banana uva --- for...in (chaves) --- "0" -> maçã | typeof string "1" -> banana | typeof string "2" -> uva | typeof string indice + 1 = 01 (texto); Number(indice) + 1 = 1 indice + 1 = 11 (texto); Number(indice) + 1 = 2 indice + 1 = 21 (texto); Number(indice) + 1 = 3 --- for...of com entries() --- 0 maçã 1 banana 2 uva --- string --- a - 1 unidade de codigo ç - 1 unidade de codigo ã - 1 unidade de codigo o - 1 unidade de codigo --- Set, sem repeticao --- pt en es --- Map --- arroz = 3 feijão = 7 --- break --- ate parar: maçã ate parar: banana --- continue --- passei: maçã passei: uva --- pedidos --- 1 Ana 2 Bia 3 Cau --- duas listas no mesmo indice --- Ana = 8 Bia = 9 Cau = 7 --- objeto com Symbol.iterator --- soma do iteravel: 30