Il classico ciclo for
Quando sai quante volte vuoi ripetere qualcosa, lo strumento giusto è il ciclo for. Racchiude le tre parti di un ciclo a conteggio (inizio, fine, passo) in un'unica riga di intestazione.
Cinque iterazioni, cinque righe di output. Leggiamo l'intestazione:
let i = 0viene eseguito una volta, prima che il ciclo inizi. Prepara il contatore.i < 5viene controllato prima di ogni iterazione. Se ètrue, il corpo viene eseguito. Se èfalse, il ciclo termina.i++viene eseguito dopo ogni iterazione, subito prima di controllare di nuovo la condizione.
Le tre parti sono separate da punto e virgola, non da virgole. Sono tutte e tre facoltative, ma ometterle è raro: in quel caso di solito si usa while.
Come si incastrano i pezzi
Seguire un ciclo a mano aiuta a fissare l'ordine:
Passo per passo:
let i = 1: il contatore viene creato e impostato a 1.- Controllo
i <= 3: vero, quindi il corpo viene eseguito. Stampa1. - Esecuzione di
i++: oraivale 2. - Controllo
i <= 3: vero. Stampa2. - Esecuzione di
i++: oraivale 3. - Controllo
i <= 3: vero. Stampa3. - Esecuzione di
i++: oraivale 4. - Controllo
i <= 3: falso. Uscita.
L'aggiornamento viene eseguito dopo il corpo, non prima. È proprio questo il punto che confonde molti.
Scorrere un array per indice
Il compito più comune di un ciclo for è scorrere un array. Il contatore fa anche da indice:
Alcune cose da notare:
- Gli indici degli array partono da zero. Il primo elemento è all'indice
0, l'ultimo alength - 1. - La condizione è
i < fruits.length, noni <= fruits.length. Con<=il ciclo andrebbe una posizione oltre la fine e stamperebbeundefined. iè dichiarata conlet, quindi esiste solo dentro il ciclo. Fuori dal ciclo non esiste.
Se ti servono solo i valori e non l'indice, for...of è più breve e più chiaro: lo vediamo in una pagina a parte.
break: fermarsi prima
break esce subito dal ciclo. È utile quando hai trovato quello che cercavi e non serve andare avanti:
Appena viene eseguito break, il controllo salta oltre la parentesi graffa di chiusura del ciclo. L'aggiornamento non viene eseguito, la condizione non viene ricontrollata: il ciclo è semplicemente finito.
continue: saltare questa iterazione
continue salta il resto dell'iterazione corrente e passa direttamente all'aggiornamento. Il ciclo continua a girare, solo che non completa il passaggio corrente.
I numeri pari incontrano il continue e saltano il console.log. Vengono stampati solo i dispari. continue è comodo quando vuoi escludere certe iterazioni senza annidare il resto del corpo dentro un if.
Passi diversi da +1
L'aggiornamento è solo un'espressione. Non deve per forza essere i++. Contare di due in due:
Contare all'indietro:
Scorrere un array al contrario, a volte utile quando rimuovi elementi strada facendo:
Qualunque cosa scegli, la regola è la stessa: condizione e aggiornamento devono lavorare insieme perché prima o poi la condizione diventi false. Altrimenti il ciclo non finisce mai. for (let i = 0; i < 10; i--) è un ciclo infinito: i si muove nella direzione sbagliata.
Cicli for annidati
Puoi mettere un ciclo for dentro un altro. Il ciclo interno viene eseguito per intero a ogni iterazione del ciclo esterno.
Nove righe di output: tre iterazioni esterne, ciascuna con tre interne. Dai a ogni contatore un nome significativo (row/col, i/j) invece di riutilizzare la stessa variabile.
Attenzione a una cosa: break e continue agiscono solo sul ciclo più interno. Uscire da un ciclo interno non ferma quello esterno. Se ti serve, imposta un flag e controllalo nel ciclo esterno, oppure sposta il lavoro annidato in una funzione ed esci con return.
Errori comuni
Alcune cose che fregano chi inizia:
Errori di uno (off-by-one). i <= arr.length fa un passo di troppo; i < arr.length - 1 si ferma un passo prima. La forma standard è i < arr.length.
Dimenticare l'aggiornamento. Se ometti i++ (o l'equivalente), il contatore non cambia mai e il ciclo gira per sempre:
for (let i = 0; i < 10; ) {
console.log(i); // never ends
}
Usare var per il contatore. var ha scope di funzione, quindi il contatore esce dal ciclo e può causare sorprese con le closure. Usa let.
Modificare l'array mentre lo scorri. Rimuovere elementi sposta gli indici e finisci per saltarne alcuni. Se devi rimuovere elementi, scorri l'array al contrario oppure creane uno nuovo con filter.
Quando usare qualcos'altro
Il classico ciclo for è sempre disponibile, ma JavaScript ha alternative più brevi per i casi comuni:
- Scorrere i valori di un array:
for (const item of array)è più pulito. - Trasformare un array:
array.map(fn)restituisce un nuovo array. - Filtrare:
array.filter(fn). - Sommare o ridurre:
array.reduce(fn, start). - Fare semplicemente qualcosa per ogni elemento:
array.forEach(fn).
Usa il classico for quando ti serve davvero l'indice, quando devi saltare elementi o interrompere a metà, oppure quando ti servono passi non standard come contare all'indietro o di due in due.
Prossimo argomento: i cicli while
Il ciclo for dà il meglio quando conosci l'intervallo in anticipo. Quando non lo conosci, cioè quando vuoi andare avanti finché una condizione non cambia, while e do...while sono più adatti. È l'argomento della prossima pagina.
Domande frequenti
Qual è la sintassi di un ciclo for in JavaScript?
Tre parti separate da punto e virgola dentro le parentesi: for (init; condizione; aggiornamento) { ... }. L'init viene eseguito una sola volta, la condizione viene controllata prima di ogni iterazione e l'aggiornamento viene eseguito dopo ogni iterazione. Un ciclo tipico è for (let i = 0; i < 10; i++) { ... }.
Come scorro un array in JavaScript?
Funziona un classico ciclo for con un indice: for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }. Se l'indice non ti serve, for...of è più pulito: for (const item of arr) { ... }. Per trasformare o filtrare, di solito lo strumento giusto sono i metodi degli array come map e filter.
Come funzionano break e continue dentro un ciclo for?
break esce subito dal ciclo: l'esecuzione salta al codice dopo il ciclo. continue salta il resto dell'iterazione corrente e passa direttamente all'aggiornamento, poi controlla di nuovo la condizione. Entrambi agiscono solo sul ciclo più interno, a meno che tu non usi le etichette (label).
Perché il mio ciclo for non finisce mai?
Di solito l'aggiornamento non porta mai la condizione verso false. for (let i = 0; i < 10; i--) conta all'indietro per sempre, perché i parte da 0 e diventa sempre più negativo. Ricontrolla condizione e aggiornamento: devono lavorare insieme perché prima o poi la condizione diventi falsa.