IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

23. diel - Cykly v JavaScripte tretíkrát

V minulej lekcii, Podmienky v JavaScripte tretíkrát, sme si ukázali ďalšie konštrukcie na tvorbu podmienok.

V dnešnom tutoriále základov JavaScriptu rozšírime naše znalosti cyklov. Naučíme sa používať cyklus do - while, príkazy break a continue a povieme si, čo je návestiou. Nakoniec si ukážeme možnosti skráteného zápisu for cyklu.

Cyklus do - while

Cyklus while už dobre poznáme. Najprv testuje podmienku a pokiaľ je od začiatku nepravdivá, telo cyklu sa nikdy nespustí. Keď tento typ cyklu v kóde použijeme, je možné, že nebude vykonaný ani raz. Oproti tomu sa cyklus do - while vykoná najmenej raz. Jeho podmienka je totiž umiestnená až za telom cyklu. Vyzerá teda takto:

do {
    // code...
} while (condition)

Náhodný trojuholník

Použitie do - while si ukážeme na príklade. Vygenerujeme trojuholník s náhodnou dĺžkou strán. Dĺžku strany vygenerujeme ako celé číslo v intervale <1,10>. Aby ale trojuholník šiel nakresliť, musí platiť veta o stranách:

Súčet ľubovoľných dvoch strán v trojuholníku je vždy väčší ako dĺžka tretej strany. Musí teda platiť a + b > c, a + c > b a b + c > a.
Strany budeme náhodne generovať tak dlho, pokiaľ nebudú splnené vyššie uvedené podmienky:
let a, b, c;
do {
    a = Math.floor(Math.random() * 10) + 1;
    b = Math.floor(Math.random() * 10) + 1;
    c = Math.floor(Math.random() * 10) + 1;
} while (a + b <= c || a + c <= b || b + c <= a);
document.write(`Triangle: a = ${a} cm, b = ${b} cm, c = ${c} cm.`);

Ukážka v prehliadači:

Do-While loop
localhost

Variant s cyklom while

Pre lepšie porovnanie si ukážme, ako by kód vyzeral s cyklom while:

let a = 0, b = 0, c = 0;
while (a + b <= c || a + c <= b || b + c <= a) {
    a = Math.floor(Math.random() * 10) + 1;
    b = Math.floor(Math.random() * 10) + 1;
    c = Math.floor(Math.random() * 10) + 1;
}
document.write(`Triangle: a = ${a} cm, b = ${b} cm, c = ${c} cm.`);

Všimnime si, že v uvedených príkladoch je podmienka v zátvorke (a + b <= c || a + c <= b || b + c <= a). Táto podmienka zaistí, že cyklus pokračuje, kým nie sú nájdené hodnoty a, b, c, ktoré možno použiť na vytvorenie platného trojuholníka. Podmienka je teda v skutočnosti negáciou citovanej vety o stranách trojuholníka.

Pri variante s cyklom while sme sa museli ešte zamyslieť nad východiskovou hodnotou premenných, ktoré sme všetky nastavili na 0, aby sa cyklus spustil. V našom prípade by sa cyklus spustil aj s hodnotami undefined, teda keby sme premenné vôbec neinicializovali, je však prehľadnejšie hodnoty uviesť.

Ukážka v prehliadači:

Do-While loop
index.html

Nasledujúca časť lekcie obsahuje menej používané praktiky. Slúžia hlavne na to, aby nás tieto praktiky neprekvapili v cudzom kóde. Nie je teraz príliš dôležité, aby sme ich sami vedeli používať.

Príkazy break a continue

Beh cyklu je potrebné niekedy prerušiť, k tomu máme nasledujúce dve kľúčové slová.

Príkaz break

Príkaz break ukončuje aktuálny cyklus. Používa sa najčastejšie, pokiaľ pomocou cyklu nájdeme nejakú položku v kolekcii a ďalej už v jej prechádzaní nechceme pokračovať. Nebudeme tak ďalej zbytočne prehľadávať zvyšok kolekcie, keď už máme to, čo sme hľadali.

Pokiaľ pracujeme s poľom, môžeme samozrejme použiť metódu indexOf(), ale niektoré kolekcie ju nemajú alebo chceme hľadať pomocou nejakej inej vlastnosti. Potom si vyhľadávanie musíme napísať ručne cyklom alebo použiť výrazne pokročilejšie konštrukcie, než teraz ovládame.

Majme teda HTML zoznam <ul> av ňom elementy <li> s nejakými textami. Budeme chcieť zmazať element obsahujúci text Cucumbers. Metódou getElementById() vyberieme zoznam a potom začneme cyklom prechádzať jednotlivé položky <li>. Akonáhle nájdeme požadovanú položku, element odstránime, zároveň pomocou break cyklus ukončíme:

<ul id ="fruit-list">
  <li>Apples</li>
  <li>Pears</li>
  <li>Cucumbers</li>
  <li>Plums</li>
</ul>

<script>
let fruitList = document.getElementById('fruit-list');
for (let fruit of fruitList.childNodes) {
    if (fruit.textContent === 'Cucumbers') {
        fruitList.removeChild(fruit);
        break;
    }
}
</script>

Ukážka v prehliadači:

Break in loop
index.html

Príkaz break sa v praxi skôr nahrádza príkazom return za predpokladu, že je kód vo funkcii. Príkaz break teda zvádza skôr k písaniu dlhých "slíži" neuniverzálneho kódu a nemali by sme ho používať.

Riešenie s príkazom return

Už sme si hovorili, že kód by sme mali členiť do funkcií. Správne by sme teda mali pre vyhľadávanie vytvoriť funkciu. V tej potom môžeme na ukončenie cyklu použiť return. Kód potom bude univerzálny a s vyhľadaným elementom pôjde urobiť čokoľvek, nielen ho odstrániť.

Prednosti varianty s return môžeme vidieť na upravenom príklade:

<ul id ="fruit-list">
  <li>Apples</li>
  <li>Pears</li>
  <li>Cucumbers</li>
  <li>Plums</li>
</ul>

<script>
function findItem(element, text) {
    for (let subelement of element.childNodes)
        if (subelement.textContent === text)
            return subelement;
}

let fruitList = document.getElementById('fruit-list');
fruitList.removeChild(findItem(fruitList, 'Cucumbers'));
</script>

Výsledok v prehliadači je rovnaký:

Your page
localhost

Príkaz continue

Príkaz continue je podobný príkazu break. Používa sa však na ukončenie iba aktuálnej iterácie (priebehu) cyklu a nie celého cyklu. Cyklus potom rovno prechádza na ďalšiu iteráciu. Použitie continue môžeme nájsť napríklad pri validovaní položiek počas prehliadania nejakej kolekcie.

Predstavme si, že máme od užívateľa zadané čísla a tieto čísla chceme sčítať. Užívateľ tieto čísla zadá ako jeden reťazec, kde je každé číslo oddelené čiarkou. Bohužiaľ musíme počítať aj s tým, že používateľ zadá namiesto čísla nejaký nezmysel. Riešenie by mohlo vyzerať nasledovne:

let commaSeparatedNumbers = '10,50,abcd,30,9';
// parsing a string into an array
let numbers = commaSeparatedNumbers.split(',');
let total = 0;
for (let number of numbers) {
    // converting a string to an integer
    let integer = parseInt(number); // returns NaN for non-numeric input
    if (isNaN(integer)) continue; // we ignore NaN values
    total += integer;
}
document.write('Total is: ' + total + '.');

Operátor += slúži na zjednodušenie zápisu, keď chceme k existujúcej hodnote premennej pripočítať nejakú ďalšiu hodnotu. Zápis a += b je teda ekvivalentom zápisu a = a + b.

Ukážka v prehliadači:

Continue in loop
localhost

Skript spočíta všetky správne zadané čísla. Pre nečíselné vstupy vráti funkcia parseInt() hodnotu NaN, tie sa potom vďaka podmienke preskočia. Namiesto continue by sme samozrejme mohli použiť len blok else, kód by sme tým však zbytočne zanorili.

Návestie (label)

Pomocou návestia si môžeme v JavaScripte pomenovať cyklus. Návestie potom využijeme pri vnorených cykloch, keď chceme ukončiť vo vnútri vnútorného cyklu vonkajší cyklus.

Syntax návestia je pomerne zmätočná. Opäť je možné elegantne obísť pomocou return za predpokladu, že je kód vo funkcii. Riešenie s return budeme teda uprednostňovať.

Príklad

Ukážme si, ako návestia vyzerajú. Predpokladajme, že máme nejakú databázu zamestnancov a budeme chcieť nájsť zamestnancov zodpovedných za dané oddelenie. Našu databázu tu bude predstavovať pole polí. Každý zamestnanec bude mať meno a vo vnorenom poli zoznam oddelení, za ktoré je zodpovedný:

let employees = [
    [ 'John Smith', [ 'pastry', 'drinks' ] ],
    [ 'Charles Wilson', [ 'electro', 'household items' ] ],
    [ 'Ava Brown', [ 'frozen products', 'dairy products' ] ]
];

Ak budeme hľadať zamestnancov zodpovedného za oddelenie elektro, musíme použiť vnorené cykly. Najprv prejdeme zamestnancov a potom ich oddelenie. Po nájdení hľadaného zamestnanca už nie je nutné prechádzať ďalšie. Ukončíme teda vonkajší cyklus z cyklu vnútorného.

Ukážka použitia návestia:

employeeLoop: for (let employee of employees) {
    for (let department of employee[1]) {
        document.write('Repeating the internal cycle.<br>');
        if (department === 'electro') {
            document.write('The electrical department is in charge of ' + employee[0] + '.<br>');
            break employeeLoop;
        }
    }
}

Ukážka v prehliadači:

Break and label in loop
localhost

Ukážka by fungovala aj bez použitia návestií, došlo by tu ale k nadbytočným prechodom cyklom, pretože by samotný break ukončil len vnútorný cyklus. Dokonca je možné aj príkaz break úplne vynechať, ale to by znamenalo ďalšie opakovanie cyklu navyše.

Riešenie s príkazom return

Lepším riešením je opäť kód vložiť do funkcie. Tú pri nájdení zamestnanca ukončíme pomocou return. Rovnako budeme časom v našom programe potrebovať nájsť zamestnancov pravdepodobne aj na inom mieste a predsa nebudeme kopírovať znova ten istý kód. Vložíme ho do funkcie a tú zavoláme všade, kde potrebujeme zamestnancov vyhľadávať.

Príklad upravíme nasledovne:

let employees = [
    [ 'John Smith', [ 'pastry', 'drinks' ] ],
    [ 'Charles Wilson', [ 'electro', 'household items' ] ],
    [ 'Ava Brown', [ 'frozen products', 'dairy products' ] ]
];

function findEmployeeByDepartment(searchedDepartment) {
    for (let employee of employees) {
        for (let department of employee[1]) {
            document.write('Repeating the internal cycle<br>');
            if (department === searchedDepartment) {
                return employee[0];
            }
        }
    }
}

let searchedEmployee = findEmployeeByDepartment('electro');
document.write('The electrical department is in charge of ' + searchedEmployee + '<br>');

Výsledok:

Your page
localhost

Ďalšie využitie príkazu return

Príkaz return je možné volať aj v prípade, že funkcia nevracia žiadnu hodnotu. Funkciu tak ukončíme. Použitie je podobné, ako pri validácii pomocou continue, kedy si ušetríme blok s podmienkou a kód je potom prehľadnejší.

Ukážme si príklad obsluhy kliknutí na tlačidlo kalkulačky z minulých lekcií a doplňme si kontrolu, či sú čísla zadané:

button.onclick = function() {
    let a = parseFloat(number1.value);
    let b = parseFloat(number2.value);
    if (isNaN(a) || isNaN(b)) {
        alert('Enter integer!');
        return;
    }
    alert(a + b);
    // ...
};

Bez znalosti tejto praktiky by sme museli dať zvyšok programu do bloku else a odsadiť hlavnú časť metódy do ďalšieho bloku. Ak by kód podmienok obsahoval viac a nie len na začiatku, mohol by byť veľmi odsadený a neprehľadný:

button.onclick = function() {
    let a = parseFloat(number1.value);
    let b = parseFloat(number2.value);
    if (isNaN(a) || isNaN(b)) {
        alert('Enter integer!');
    else {
        alert(a + b);
        // ...
    }
};

Pokiaľ funkcia vracia nejakú hodnotu, nikdy v nej nevolejme samotné return. Funkcia by buď mala vždy niečo vracať, alebo nevracať nikdy nič. Použitie funkcie, ktorá vracia hodnotu len niekedy, je potenciálny zdroj chýb v programe.

Skrátený zápis cyklu for

Nasledujúce konštrukcie sú tu pre ukážku, čo všetko je možné stretnúť v cudzích kódoch a nie je dobrý dôvod ich používať!

Cyklus for je možné zapísať takto skrátene, bez tela cyklu:

for (let i = 0; i < 10; document.write(i++));

Ukážka v prehliadači:

For loop
localhost

Zapisovať ako priebeh cyklu, tak aj logiku vo vnútri cyklu na jeden riadok nie je príliš intuitívne. Navyše sa tak môže ľahko zabudnúť na inkrementáciu premennej alebo ju budeme omylom inkrementovať viackrát.

V hlavičke cyklu for dokonca nie je nutné uvádzať akýkoľvek príkaz:

for (;;) {
    // endless loop
}

Tento zápis je rovnaký ako:

while (true) {
    // endless loop
}

Oba príklady povedú k zaseknutiu vlákna danej záložky prehliadača! Oba vyššie deklarované cykly bežia do nekonečna a môžeme ich stretnúť v zle napísaných zdrojových kódoch spolu s príkazmi break, ktoré z nich potom za nejakých podmienok vyskakujú.

Akonáhle podmienka nie je priamo v deklarácii cyklu, je pomerne neprehľadné zistiť, kedy cyklus vôbec skončí. Je potom ľahké urobiť z takého cyklu nechtiac nekonečný, zvlášť, keď z neho vyskakujeme viacerými podmienkami a nepokryjeme všetky možné prípady.

V nasledujúcom cvičení, Riešené úlohy k 15.-23. lekciu JavaScriptu, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 2x (3.42 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Podmienky v JavaScripte tretíkrát
Všetky články v sekcii
Základné konštrukcie jazyka JavaScript
Preskočiť článok
(neodporúčame)
Riešené úlohy k 15.-23. lekciu JavaScriptu
Článok pre vás napísal Roman
Avatar
Užívateľské hodnotenie:
5 hlasov
Roman
Aktivity