for Kurs Javascript - pętle

Pętle

Pętlę w programowaniu pozwalają nam wykonywać dany kod pewną 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.

Pętla typu for

Jednym z najczęściej stosowanych typów pętli jest instrukcja for


for (zainicjowanie_licznika;  warunek_kończący_wykonywanie_pętli;  zwiększenie_zmniejszenie_licznika) {
    kod który zostanie wykonany pewną ilość razy
}

Przykładowo:


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(i);
}


let sum = 0;
for (let i=0; i<10; i++) {
    sum += i;
}
console.log(sum); //wypisze 45

Pętle spokojnie mogą odliczać w przeciwnym kierunku:


let str = "";

for (let i=5; i>0; i--) {
    str += "Trwa odliczanie: "+i+" \n";
}

console.log(str);

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++) {
    console.log("Wypiszę się tyle co ma mniejsza liczba", i);
}

Pętla typu while

Pętla while to kolejny typ pętli stosowany w JavaScript. 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);

Taki typ pętli 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.

Popatrz na powyższe przykłady. Pętla zaczyna się "kręcić", wykonując swój wewnętrzny kod. W powyższych przykładach to tylko jedna linijka console.log. Ale przecież takich linii kodu, który ma powtarzać pętla 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 y=0; y<6; y++) {
        console.log('%c Pętla wewnętrzna nr: ' + y, 'color:blue');
    }

}

Działanie powyższego skryptu ma postać:

Pętla w pętli

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 przeciwnym razie praktycznie zawsze czeka cię zawieszenie strony.

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<3; 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);

Pętle wykonywane po tablicach

Nie zawsze wiemy ile dany obiekt ma elementów, i ile razy nasza pętla się wykona. A czasami zwyczajnie nam się nie chce liczyć.

Taka sytuacja pojawia się na przykład w przypadku stringów. Żeby pobrać długość tekstu wystarczy skorzystać z właściwości length:


const txt = "Ala ma kota";

for (let i=0; i<txt.length; i++) {
    console.log(  txt.charAt(i)  ); //do pobrania litery na danej pozycji używamy charAt()
}

Podobne działania będą w przypadku tablic. Tutaj także istnieje właściwość length, która zwraca długość tablicy (liczbę elementów które zawiera dana tablica):


const tab = ['Ala', 'Monika', 'Patrycja'];

for (let i=0; i<tab.length; i++) {
    console.log( tab[i] );
}

Ta sama zasada tyczy się przy pracy z pobranymi ze strony elementami. Gdy pobieramy takie elementy, często nie wiemy ile ich jest. Wystarczy zrobić więc pętle po takiej "tablicy":


const p = document.querySelectorAll('p'); //pobieramy wszystkie paragrafy z dokumentu

for (let i=0; i<p.length/2; i++) {
    p[i].style.color = 'red'; //każdy paragraf na czerwono
}

Pętla for of

W wersji ES6 istnieje też o wiele wygodniejszy sposób na iterowanie po tablicach, a jest nim pętla for of, która służy do iterowania po zmiennych iterowalnych (stringi, kolekcje, tablice itp).


const tab = [1,2,3,4];

for (const el in tab) {
    console.log(el);
}


const buttons = document.querySelectorAll('button');
for (const btn of buttons) {
    console.log(btn);
}


const txt = "ALa ma kota";
for (const i of txt) {
    console.log(i.toUpperCase());
}

Należy tutaj tylko pamiętać, że jeżeli zmuszeni jesteśmy do wspierania starszych przeglądarek, ta metoda odpada na rzecz klasycznej pętli for (chyba że użyjemy czegoś na wzór gulpa lub webpacka).

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.


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ć
    }
}

let evenNr = null;
while (true) { //nieskończony warunek
    evenNr = Math.floor(Math.random() * 1000);

    if (evenNr % 2 === 0) {
        break;
    }
}

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 n = 0;
while (i < 5) {
  i++;
  if (i === 3) {
    continue;
  }
  n += i;
}

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. Poniższa pętla jest nieskończona:


let i = 0;
let n = 0;
while (i < 5) {
  if (i === 3) {
    continue; //gdy i === 3 przerywamy poniższy kod, i przy 3 już nidgy się nie zwiększy
  }
  n += i;
  i++;
}

Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać:

  1. Za pomocą pętli wypisz w konsoli tekst
    ******
    Gwiazdki powinny być w jednej linii. Skorzystaj tutaj z dodatkowej zmiennej str.
    
                let str = "";
                for (var i=0; i<6; i++) {
                    str += "*";
                }
                console.log(str);
                
            
  2. Za pomocą pętli wypisz w konsoli tekst
    12345678910
    
                let str = "";
                for (var i=1; i<=10; i++) {
                    str += i;
                }
                console.log(str);
                
            
  3. Za pomocą pętli w pętli wypisz w konsoli tekst:
    ******
    ******
    ******
    ******
    
                let str = "";
                for (var i=0; i<6; i++) {
                    for (var j=0; j<6; j++) {
                        str += "*";
                    }
                    str += "\n";
                }
                console.log(str);
                
            
  4. Za pomocą pętli (nie musi być pojedyncza) wypisz w konsoli tekst:
    ╔═══════════════════════╗
    ║  To jest jakiś tekst  ║
    ╚═══════════════════════╝

    W razie czego długość tekstu pobierzesz za pomocą konstrukcji:

    
            "Ala ma kota".length //11
            
    
                const text = "Ala ma kota";
                let str = "";
    
                //gora
                str += "╔";
                //do długości tekstu dodajemy 4 spacje oddzielające go od bocznych krawędzi
                for (var i=0; i<text.length+4; i++) {
                    str += "═";
                }
                str += "╗\n";
    
                //srodek
                str += "║  " + text + "  ║\n";
    
                //dol
                str += "╚";
                //do długości tekstu dodajemy 4 spacje oddzielające go od bocznych krawędzi
                for (var i=0; i<text.length+4; i++) {
                    str += "═";
                }
                str += "╝\n";
    
                console.log(str);