Pętle for i while
Pętlę w programowaniu pozwalają nam wykonywać dany kod zadaną ilość razy.
Przypuśćmy, że byliśmy niegrzeczni i nauczyciel kazał nam napisać jakieś zdanie 100 razy. Możemy to oczywiście zrobić za pomocą poniższego kodu:
console.log("Nie będę rozmawiał na lekcji Informatyki.");
console.log("Nie będę rozmawiał na lekcji Informatyki.");
console.log("Nie będę rozmawiał na lekcji Informatyki.");
console.log("Nie będę rozmawiał na lekcji Informatyki.");
console.log("Nie będę rozmawiał na lekcji Informatyki.");
...
Ale o wiele lepiej jest skorzystać z pętli, która wykona dany kod zadaną liczbę razy.
Poniżej zajmiemy się klasycznymi pętlami, które istnieją w większości języków - w tym w Javascript.
Pętla typu for
Jednym z najczęściej stosowanych typów pętli jest instrukcja for.
for (zainicjowanie_zmiennych; warunek_kończący_wykonywanie_pętli; zmiana_zmiennych) {
kod który zostanie wykonany pewną ilość razy
}
Pętle takie najczęściej stosuje się w sytuacjach, kiedy dokładnie znamy liczbę powtórzeń - od - do:
//pętla od 0 do 99
for (let i=0; i<100; i++) {
console.log("Nie będę rozmawiał na lekcji Informatyki.");
}
W każdej pętli mamy dostęp do jej licznika:
for (let i=0; i<10; i++) {
console.log("Wykonanie pętli ", i);
}
let sum = 0;
for (let i=0; i<10; i++) {
sum += i;
}
console.log(sum); //45
Pętle spokojnie mogą odliczać w przeciwnym kierunku:
for (let i=10; i>0; i--) {
console.log("Trwa odliczanie", i);
}
A sam warunek kończący wcale nie musi wyglądać jak powyżej:
const a = 10;
const b = 20;
for (let i=1; i<=a && i<=b; i++) {
//bo oba muszą być prawdziwe
console.log("Wypisze się tyle co ma mniejsza liczba", i);
}
Jeżeli nie potrzeba, to możemy pominąć dowolną z trzech składowych pętli. Nie używasz licznika? Nie deklarujemy zmiennych. Nie potrzebujesz kończącego warunku? Nie twórz go.
let a = 10;
let i = 0;
for (; i<10 ;) {
console.log(i);
i++;
}
for (let j=0; j<10; ) {
console.log(j);
j++;
}
Tak naprawdę nic nie musimy tutaj podawać, a sama pętla może mieć postać:
for (;;) {
}
Widziałem to tylko w jednym miejscu w Internecie - w dokumentacji MDN. Pętla taka staje się nieskończoną pętlą. Na co dzień polecam jednak podawać wszystkie składowe. Czytelność i jasność kodu znacząco się powiększa.
Pętla typu while
Pętla while (dopóki) to kolejny typ pętli, który można spotkać w wielu językach. Struktura tej pętli ma następującą postać:
while (wyrażenie_sprawdzające_zakończenie_pętli) {
...fragment kodu który będzie powtarzany...
}
Zauważ, że w pętli tego typu nie definiujemy ani początkowego licznika, ani nie definiujemy zmiany licznika. Musimy te rzeczy zrobić ręcznie:
let i = 1;
while (i <= 100) {
console.log("Nie będę...");
i++;
}
Jeżeli w powyższym kodzie pętli nie zwiększalibyśmy zmiennej i, wówczas pętla ta wykonywała by się w nieskończoność (infinite loop), co zaowocowało by "zawieszeniem" strony.
Pętlę while zazwyczaj stosuje się w sytuacjach, kiedy nie wiemy dokładnie, ile iteracji (powtórzeń) ma się wykonać. Wyobraź sobie, że chcesz wygenerować unikalny numer ID albo jakąś liczbę. Generujesz więc daną rzecz, następnie sprawdzasz czy wynik pasuje do założeń. Jak nie pasuje, generujesz dalej. I tak do skutku aż trafisz.
let i = 0;
while (i < 0.5) {
console.log(i);
i = Math.random();
}
console.log(i);
Istnieje też nieco inny wariant pętli while:
let i = 0;
do {
i++;
console.log(i);
} while (i < 5); //wykona się minimum 1 raz
let i = 0;
do {
i++;
console.log(i);
} while (false); //warunek od początku nie spełniony ale i tak 1 raz się wykona
Pętla w pętli
Czasami musimy wykonać zadania "n - wymiarowe".
Dla przykładu przy wypisywaniu tabliczki mnożenia musimy utworzyć 10 kolumn z 10 komórkami. Do takich działań stosujemy pętle w pętlach.
W powyższych przykładach wykonywaliśmy najczęściej tylko jedno console.log()
. Ale przecież takich linii kodu, które mają się powtarzać może być dowolnie wiele. Co więcej - wśród tych linii mogą być zagnieżdżone pętle, które przy każdym przebiegu głównej pętli wykonają swój kod naście razy.

for (let i=0; i<10; i++) {
console.log("%c Główna pętla nr: " + i, "color:red");
for (let j=0; j<6; j++) {
console.log("%c Pętla wewnętrzna nr: " + j, "color:blue");
}
}
Działanie powyższego skryptu ma postać:

Jak widzisz główna pętla zaczyna działać wykonując po kolei kolejne linie kodu. Wypisuje więc w konsoli jedną czerwoną linijkę. Kod dochodzi do wewnętrznej pętli. Ta zaczyna działać.
Po jej zakończeniu kończy się działanie jednej iteracji głównej pętli. Rozpoczyna się druga iteracja...
Co ważne, wewnętrzne pętle mają dostęp do liczników pętli zewnętrznych.
Bardzo ważne jest by pętle wewnętrzne miały inny licznik niż główna pętla.
W przypadku stosowania zmiennych var użycie takiego samego licznika praktycznie zawsze oznacza zawieszenie strony.
W przypadku let można niby użyć licznik o takiej samej nazwie, ale tracimy wtedy dodatkowe możliwości:
for (let i=0; i<10; i++) {
for (let j=0; j<6; j++) {
console.log(`Główna pętla: ${i}, pętla zagnieżdżona: ${j}`)
}
}
for (let i=0; i<10; i++) {
for (let i=0; i<6; i++) {
//nie mam dostępu do zewnętrznego licznika...
console.log(`Pętla zagnieżdżona: ${i}`);
}
}
Przykłady użycia pętli
Powiedzmy, że chcemy w konsoli wypisać tekst:
******
Wykorzystajmy do tego pętlę:
let str = "";
for (let j=0; j<6; j++) {
str += "*";
}
console.log(str);
Dodajemy kolejny poziom skomplikowania. Załóżmy, że chcemy wypisać w konsoli poniższy tekst:
******
******
******
******
Użyjmy do tego pętli w pętli:
let str = "";
for (let i=0; i<4; i++) {
for (let j=0; j<6; j++) {
str += "*";
}
str += "\n";
}
console.log(str);
Pozostaje poziom hard. Załóżmy że chcemy w konsoli wypisać:
******
*----*
*----*
******
Użyjmy do tego pętli w pętli, w której zbadamy dane liczniki:
let str = "";
for (let i=0; i<4; i++) {
for (let j=0; j<6; j++) {
if (i===0 || i===3 || j===0 || j===5) {
str += "*";
} else {
str += "-";
}
}
str += "\n";
}
console.log(str);
Break i continue
Czasami podczas pętli gdy wykonamy jakąś czynność - czy to wypiszemy dany element, czy go sprawdzimy, chcielibyśmy zakończyć dalsze wykonywanie takiej pętli. Służy do tego instrukcja break.
let str = '';
let i = 0;
while (i <= 100) {
str += i;
if (str.length > 20) break;
i++;
}
console.log(str);
const tab = ["Ala", "Monika", "Beata", "Karol"];
let userExist = false;
for (let i=0; i<tab.length; i++) {
if (tab[i] === "Beata") {
userExist = true;
break; //dalej nie ma sensu sprawdzać
}
}
//to samo uzyskamy o wiele lepiej za pomocą includes(), indexOf() czy findIndex()
let nr = -1;
for (let i=0; i<1000; i++) {
nr = Math.random();
if (nr >= 0.95) break;
}
console.log(nr);
Drugą instrukcją jest continue. Nie przerywa ona działania pętli, a powoduje przerwanie danej iteracji (czyli aktualnego powtórzenia):
const tab = ["Ala", "Monika", "Beata", "Karol", "Alicja"];
for (let i=0; i<tab.length; i++) {
if (tab[i] === "Karol") {
continue; //Karola pomiń
}
console.log(tab[i]);
}
let i = 0;
let sum = 0;
while (i < 5) {
i++;
if (i === 3) continue;
sum += i;
console.log(i, `suma kolejnych liczb to ${sum}`);
}
Zwróć uwagę, że gdy stosujemy continue w pętli while, zwiększanie licznika musimy robić przed użyciem tej instrukcji. Inaczej możemy trafić na moment, gdy aktualne powtórzenie będzie przerywane a tym samym zwiększanie licznika nigdy nie nastąpi.
let i = 0;
let sum = 0;
while (i < 100) {
i++;
if (i % 2 === 0) continue; //gdy i jest parzyste przerywamy daną iterację i przechodzimy do następnej
sum += i;
}
console.log(i, `suma kolejnych liczb to ${sum}`);
Labele dla pętli
Każda pętla może być dodatkowo nazwana za pomocą etykiet. Dzięki nim możemy stosować instrukcje break i continue dla pętli o danej nazwie:
for (let i=0; i<10; i++) {
for (let j=0; j<10; j++) {
if (warunek) break; //normalnie mogę przerwać tylko pętlę w której użyłem instrukcji break/continue
}
}
first:
for (let i=0; i<10; i++) {
second:
for (let j=0; j<10; j++) {
if (warunek) break first; //przerywam główną pętlę
}
}
loopA: for (let i=0; i<10; i++) {
loopB: for (let j=0; j<10; j++) {
if (warunek) continue loopA;
}
}
Przy czym funkcjonalność ta jest tak skrajnie rzadko używana, że prawdopodobnie nigdy się z nią nie zetkniesz...
Edit: Tak naprawdę składnia ta zyskuje na popularności za sprawą https://svelte.dev/, który używa jej oznaczania reaktywnych deklaracji:
const a = 100;
const b = 200;
$: const nr = a * b; //label która ma nazwę "$"
Trening czyni mistrza
Jeżeli chcesz sobie potrenować zdobytą wiedzę, zadania znajdują się w repozytorium pod adresem: https://github.com/kartofelek007/zadania