Skip to content
This page has been auto-translated and may contain errors.View in English

Loops

docs.scrimba.com

Digamos que você queira imprimir os números de 1 a 100, ou saudar cada caractere de uma palavra, ou continuar fazendo uma pergunta até que a resposta esteja correta. Você poderia escrever a mesma linha cem vezes, mas isso é tedioso de digitar e pior ainda de alterar. Um loop é como você diz ao JavaScript para executar um pedaço de código novamente e novamente, então você escreve o trabalho uma vez e deixa a linguagem fazer a repetição. Este capítulo cobre os três loops que você usa mais: for, while e for...of.

O loop for

O loop for é aquele que você usa quando sabe quantas vezes repetir. Ele conta para você. Aqui ele está imprimindo os números de 0 a 4:

js
for (let i = 0; i < 5; i++) {
  console.log(i);
}
// 0
// 1
// 2
// 3
// 4

A parte entre parênteses tem três pedaços, separados por ponto-e-vírgula:

  • let i = 0 é o início: cria um contador chamado i e o define como 0.
  • i < 5 é a condição: o loop continua enquanto isso for verdadeiro.
  • i++ é o passo: após cada passagem, adiciona 1 a i (i++ é abreviação para i = i + 1).

Então o loop começa i em 0, verifica que i < 5, executa o código entre chaves, depois adiciona 1 e verifica novamente. Quando i atinge 5 a condição é falsa e o loop para.

O loop for é feito para o caso em que você conhece o número de repetições antecipadamente. Ele compacta três pedaços em seu cabeçalho, executados em ordem:

js
for (let i = 0; i < 5; i++) {
  console.log(`pass number ${i}`);
}
// pass number 0
// pass number 1
// pass number 2
// pass number 3
// pass number 4

Lendo o cabeçalho da esquerda para a direita: let i = 0 é o inicializador, executado uma vez antes de qualquer outra coisa. i < 5 é a condição, verificada antes de cada passagem, e o loop só corre enquanto ela for verdadeira. i++ é o passo, executado após cada passagem. O contador i é uma variável normal, então você pode lê-lo dentro do loop, é por isso que loops e contagem andam juntos.

O contador começa em 0 por convenção, não por regra. Contar a partir de 0 é o que você verá em quase todos os lugares em JavaScript, e se alinha com a forma como arrays numeraram seus itens, então vale a pena se acostumar cedo.

O loop for codifica toda a forma de uma repetição contada em um cabeçalho: inicializador, condição e passo, cada um separado por ponto-e-vírgula.

js
for (let i = 0; i < 5; i++) {
  console.log(`pass number ${i}`);
}
// pass number 0
// pass number 1
// pass number 2
// pass number 3
// pass number 4

A ordem é exata e vale a pena internalizar: o inicializador é executado uma vez, depois a condição é testada, e apenas se for verdadeira o corpo é executado, seguido pelo passo, depois volta à condição. Essa ordem é por que a condição é verificada antes da primeira passagem, então um loop cuja condição começa falsa nunca executa seu corpo.

Use let para o contador, não const, porque o passo o reatribui a cada passagem. E declarar i no cabeçalho o limita ao escopo do loop, então ele não vaza para o código ao redor, o que evita colisões de contadores quando você aninha loops ou escreve vários em sequência. Contar a partir de 0 em vez de 1 é a convenção quase universal aqui, e mapeia diretamente para como arrays indexam seus elementos, então o hábito compensa no momento em que você faz loop sobre dados.

JunoO loop for O for loop repete um número fixo de vezes e conta para você. Seu cabeçalho tem três partes: um início como let i = 0, uma condição como i < 5 que o mantém funcionando, e um passo como i++ que o move adiante. Quando a condição fica falsa, o loop para.
JunoO loop for Use for quando você conhece a contagem antecipadamente. O cabeçalho executa o inicializador uma vez, verifica a condição antes de cada passagem e executa o passo após cada uma. Contar a partir de 0 é a convenção, e se alinha com a forma como arrays numeraram seus itens, então use essa abordagem desde agora.
JunoO loop for A ordem do cabeçalho é fixa: inicializar uma vez, testar a condição, executar o corpo, executar o passo, repetir. É por isso que uma condição começando falsa pula o corpo inteiramente. Use let para o contador para que o passo possa reatribuí-lo, e declare-o no cabeçalho para que ele permaneça no escopo do loop.

O loop while

Às vezes você não sabe quantas vezes repetir, você só sabe uma condição que deve permanecer verdadeira. É para isso que o loop while existe. Ele continua funcionando enquanto sua condição se mantiver:

js
let count = 3;
while (count > 0) {
  console.log(count);
  count = count - 1;
}
console.log("Go");
// 3
// 2
// 1
// Go

O loop verifica count > 0, executa o código, depois verifica novamente. Observe que algo dentro do loop muda count, então a condição eventualmente fica falsa e o loop para. Se nada dentro nunca tornar a condição falsa, o loop corre para sempre. Isso é um loop infinito, e congelará seu programa, então sempre certifique-se de que a condição pode mudar.

Um loop while repete enquanto uma condição permanece verdadeira, e não possui um contador ou passo integrado. Isso o torna a ferramenta certa quando o número de repetições não é conhecido antecipadamente, e um for loop a ferramenta certa quando é.

js
let attempts = 0;
let password = "";
while (password !== "letmein") {
  password = "letmein"; // substituto para um prompt real
  attempts = attempts + 1;
}
console.log(`Unlocked after ${attempts} attempt(s)`);
// Unlocked after 1 attempt(s)

A condição (password !== "letmein") se baseia nos operadores de comparação de condicionais, e o loop corre até ela ficar falsa. O problema é que nada a força a ficar falsa: você tem que mudar algo dentro do loop que a condição depende. Um loop while cuja condição nunca fica falsa corre para sempre. Isso é um loop infinito, e congela o programa.

Um loop while é a forma geral: repete enquanto uma condição é verdadeira e deixa a contagem, se houver, inteiramente para você. Use-o quando o número de iterações não é fixo, por exemplo repetindo até que a entrada valide ou um valor cruze um limiar. Quando a contagem é conhecida, um for loop deixa essa intenção mais clara.

js
let remaining = 100;
let steps = 0;
while (remaining > 0) {
  remaining = remaining - 15;
  steps = steps + 1;
}
console.log(`Emptied in ${steps} steps`);
// Emptied in 7 steps

O modo de falha a respeitar é o loop infinito: while não lhe dá nenhuma garantia estrutural de término, então o corpo deve mover a condição em direção a falsa. Três coisas causam um loop pendurado: uma condição que nunca é atualizada, uma atualização que se move na direção errada, e uma condição que pode ser pulada em vez de atendida (usar remaining === 0 acima falharia, pois remaining chega a um número negativo, não exatamente 0). Todo loop while precisa de uma linha que empurre sua condição em direção a falsa. Prefira um teste de intervalo como > 0 em vez de um === 0 exato para que um passo que ultrapassa ainda termine o loop.

JunoO loop while Um while loop repete enquanto sua condição é verdadeira, sem contador integrado. Use-o quando você não conhece o número de repetições antecipadamente. Sempre mude algo dentro do loop para que a condição possa ficar falsa, ou você terá um loop infinito que congela o programa.
JunoO loop while Escolha while quando o número de repetições é desconhecido, e for quando é fixo. O corpo deve mudar o que a condição testa, ou nunca fica falso. Esse é o loop infinito, e é o erro a observar toda vez que você escreve um while.
JunoO loop whilewhile não oferece garantia de que nunca para, então o corpo deve empurrar a condição em direção a falso. Três maneiras de pendurar: a condição nunca é atualizada, se move na direção errada, ou fica pulada. Prefira um teste de intervalo como > 0 em vez de um === 0 exato para que um passo que ultrapassa ainda termine o loop.

Iteração sobre uma string com for...of

Os loops for e while contam com um número, mas frequentemente você quer visitar cada item em algo diretamente. for...of faz isso. Aqui ele percorre cada caractere de uma palavra:

js
for (const char of "hello") {
  console.log(char);
}
// h
// e
// l
// l
// o

Leia como "para cada caractere da palavra". A cada passagem, char contém o próximo caractere, e você nunca toca em um contador ou uma condição. É mais limpo que um loop for quando tudo que você quer é olhar para cada item em sequência.

Você usará principalmente for...of em arrays, que são listas de valores que você conhecerá nos próximos capítulos. Uma string é uma boa primeira coisa para fazer loop porque já é uma sequência de caracteres, e for...of a trata exatamente assim.

for...of itera sobre os itens de uma sequência diretamente, entregando-lhe cada valor em sequência sem um contador ou uma condição para gerenciar:

js
let vowels = 0;
for (const char of "javascript") {
  if (char === "a" || char === "i") {
    vowels = vowels + 1;
  }
}
console.log(`Found ${vowels} of those vowels`);
// Found 3 of those vowels

Usar const char aqui é deliberado: cada passagem cria um char novo que você só lê, nunca reatribui, então const se encaixa e sinaliza essa intenção. Um loop for...of negocia a contabilidade manual de um loop contador por legibilidade, e remove uma classe inteira de erros off-by-one porque você nunca indexa manualmente.

Strings são uma sequência natural para fazer loop, mas o grande trabalhador é o array. Uma vez que você conhece arrays, for...of é a forma limpa e direta de percorrer seu conteúdo.

for...of itera qualquer sequência elemento por elemento, vinculando cada valor a um nome sem expor um índice. Em uma string produz caracteres:

js
let shout = "";
for (const char of "loops") {
  shout = shout + char.toUpperCase();
}
console.log(shout);
// LOOPS

Vincule a variável do loop com const: for...of cria uma vinculação nova a cada passagem, então nada precisa reatribuí-la, e const documenta que o valor é somente leitura dentro do corpo. O ganho sobre um loop contador é que você nunca escreve um índice, o que evita erros off-by-one por construção.

Onde isso mais importa é em arrays. Para visitar cada elemento, for...of lê melhor que um loop for contador e enuncia a intenção diretamente. Mas quando você está transformando uma coleção em uma nova, os métodos de iteração de array geralmente substituem o loop manual inteiramente, e eles são cobertos em seu próprio capítulo.

break e continue

Duas palavras-chave lhe dão controle mais fino dentro de qualquer loop. break para o loop imediatamente e segue para o código depois dele. continue pula o resto da passagem atual e salta direto para a próxima.

js
for (let i = 0; i < 10; i++) {
  if (i === 3) {
    continue; // pula 3, continua looping
  }
  if (i === 6) {
    break; // para o loop inteiramente
  }
  console.log(i);
}
// 0
// 1
// 2
// 4
// 5

break é como você para cedo uma vez que encontrou o que precisa, então não continua escaneando inutilmente. continue é como você pula casos que não se importa sem aninhar o resto do corpo dentro de um if.

Erros off-by-one e limites de índice

O bug de loop clássico é o erro off-by-one: rodar uma vez a mais ou uma a menos. Vem dos limites do contador. Uma string de comprimento 5 tem caracteres nas posições 0, 1, 2, 3 e 4, então a última posição válida é length - 1, não length. É por isso que um loop contador sobre suas posições usa i < text.length, não i <= text.length:

js
const text = "hello";
for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// h
// e
// l
// l
// o

Escreva i <= text.length e a última passagem atinge a posição 5, que não existe, então produz undefined. Contar de 0 com < até o comprimento é o padrão que chega exatamente a cada posição e nada mais. Quando você só precisa de cada item e não de sua posição, for...of evita a questão dos limites completamente, o que é um motivo para preferi-lo.

JunoIteração sobre uma string com for...offor...of visita cada item diretamente, sem contador e sem condição para gerenciar. Em uma string, cada passagem lhe dá o próximo caractere. Você o usará mais em arrays depois, mas uma string é uma primeira coisa limpa para fazer loop porque já é uma sequência de caracteres.
JunoIteração sobre uma string com for...offor...of lhe entrega cada valor em sequência, então você pula o contador e o risco off-by-one que vem com indexação manual. Vincule com const pois cada passagem é somente leitura. Strings são um bom começo, mas arrays são onde você vai confiar nela uma vez que os conhece.
JunoIteração sobre uma string com for...offor...of vincula cada elemento a um const novo e nunca expõe um índice, o que remove bugs off-by-one por construção. break para cedo, continue pula uma passagem. Para transformar uma coleção em vez de apenas percorrê-la, os métodos de iteração de array geralmente substituem o loop manual, e eles recebem seu próprio capítulo.

De onde loops vão daqui

Loops são como você para de se repetir e deixa a linguagem fazer a repetição. O loop for conta um número conhecido de vezes, while repete enquanto uma condição se mantém, e for...of percorre os itens de uma sequência diretamente. O único hábito a levar daqui: certifique-se de que cada loop pode terminar, e prefira for...of quando você só precisa de cada item.

A razão real pela qual loops importam é que a maioria dos dados com que você trabalha vem em coleções. Os próximos capítulos apresentam arrays, as listas que você fará loop constantemente, e depois os métodos de array que frequentemente substituem um loop escrito manualmente quando você está transformando dados. Se você quiser afiar as condições em que seus loops dependem, condicionais cobre as comparações que decidem quando um loop continua e quando para.