IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

14. diel - Cykly v JavaScripte druhýkrát

V dnešnej lekcii rozšírime naše znalosti cyklov v JavaScripte. Naučíme sa používať cyklus do... while, príkazy break a continue, návestí a nakoniec si ukážeme možnosti skráteného zápisu for cyklu.

Na úvod by bolo dobré podotknúť, že dnešná lekcia obsahuje menej používané praktiky a slúži hlavne na to, aby vás neprekvapili v cudzom kódu. Nie je príliš dôležité, aby ste ich sami vedeli používať.

do... while

Cyklus while už dobre poznáme. Menej používaný do... while sa odlišuje len tým, že sa vždy vykoná najmenej raz. Jeho podmienka je totiž umiestnená až za telom cyklu. Vyzerá teda takto:

do {
    // kód ...
} while (podmienka)

Príklad

Použitie do - while si ukážme na príklade. Vygenerujeme trojuholník o náhodné dĺžke 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 2 strán v trojuholníku je vždy dlhšia než tretia strana, 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. V kóde sa objaví špeciálny znak ` (tzv. Tupý prízvuk).

Na slovenskej klávesnici ho môžeme napísať pomocou pravého ALT a písmena ý:

tupý prízvuk - Základné konštrukcie jazyka JavaScript

Použijeme taký znak dolára $. Na klávesnici ho napíšeme pomocou pravého ALT a písmena ov:

dolár - Základné konštrukcie jazyka JavaScript

Teraz pomocou cyklu do - while vygenerujeme náhodné dĺžky strán takto:

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(`Trojuholník: a = ${a} cm, b = ${b} cm, c = ${c} cm.`);

Ukážka v prehliadači:

Cyklus do while
index.html

Variant s cyklom while

Pre lepšie porovnanie si ukážme aj ako by kód vyzeral s nami už známym 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(`Trojuholník: a = ${a} cm, b = ${b} cm, c = ${c} cm.`);

Ukážka v prehliadači:

Cyklus while
index.html

Všimnite si, že sme sa pri variante s while museli zamyslieť nad východiskovou hodnotou premenných, ktoré sme všetky nastavili na 0, aby podmienka nebola ihneď splnená. V prípade trojuholníku by sa podmienka nesplnila ani pre hodnoty undefined, teda keby sme premenné vôbec neinicializovali, je však prehľadnejšie hodnoty uviesť. Vymyslieť predvolenú hodnotu môže byť niekedy pomerne zložité a aj vyžadovať pomocnú premennú.

break a continue

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

break

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

Príklad

Predstavme si, že máme zoznam položiek a chceme v nich nejakú nájsť. Že môžeme použiť metódu indexOf() ? Pokiaľ pôjde o pole, tak áno, ale niektoré kolekcie ju nemajú a / alebo chceme hľadať pomocou nejakej iné vlastnosti, než ktorú indexOf() zohľadňuje. Potom si vyhľadávanie musíme napísať pekne ručne cyklom alebo použiť výrazne pokročilejšie konštrukcie, ako teraz ovládame.

Majme teda HTML zoznam <ul> a v ňom elementy <li> s nejakými textami. Budeme chcieť zmazať element obsahujúce textUhorky. 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. Zatiaľ stále nič nové pod slnkom. V tú istú chvíľu však aj pomocou break cyklus ukončíme.

Ukážka použitia break:

<ul id ="seznam-ovoce">
  <li>Jablká</li>
  <li>Hrušky</li>
  <li>Uhorky</li>
  <li>Slivky</li>
</ul>
<script>
let seznamOvoce = document.getElementById('seznam-ovoce');
for (let ovoce of seznamOvoce.childNodes) {
    if (ovoce.textContent === 'Uhorky') {
        seznamOvoce.removeChild(ovoce);
        break;
    }
}
</script>

Ukážka v prehliadači:

Cyklus s break
index.html

Príkaz break sa v praxi skôr nahrádza príkazom return za predpokladu, že je kód vo funkcii, viď ďalej. Preto odporúčam break nepoužívať.

Príklad pomocou 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 k ukončeniu cyklu použiť return a ešte bude univerzálny as vyhľadaným elementom pôjde urobiť čokoľvek, nielen ho odstrániť.

break teda zvádza skôr k písanie dlhých "rezancov" neuniverzálního kódu a nemali by sme ho používať. Prednosti varianty s return môžete vidieť na upravenom príklade nižšie:

<ul id ="seznam-ovoce">
  <li>Jablká</li>
  <li>Hrušky</li>
  <li>Uhorky</li>
  <li>Slivky</li>
</ul>
<script>

function najdiPolozku(element, text) {
    for (let podelement of element.childNodes)
        if (podelement.textContent === text)
            return podelement;
}

let seznamOvoce = document.getElementById('seznam-ovoce');
seznamOvoce.removeChild(najdiPolozku(seznamOvoce, 'Uhorky'));
</script>

Výsledok v prehliadači je rovnaký:

Tvoja stránka
localhost

continue

Príkaz continue je podobný break. Používa sa však k ukončeniu 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. Pri validovanie položiek pri prehliadaní nejakej kolekcie.

Príklad

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 každé číslo je oddelené čiarkou. Bohužiaľ musíme počítať aj s tým, že používateľ zadá namiesto čísla nejaký nezmysel alebo 0. Riešenie by mohlo vyzerať nasledovne:

let cislaRetezec = '10,50,abcd,30,9';
// rozloženie reťazca do poľa
let cislaPole = cislaRetezec.split(',');
let soucet = 0;
for (let cislo of cislaPole) {
    // prevedenie reťazca na celé číslo
    let celeCislo= parseInt(cislo);
    if (Number.isNaN(celeCislo)) continue; // hodnoty NaN ignorujeme
    soucet += celeCislo;
}
document.write('Súčet je: ' + soucet + '.');

Ukážka v prehliadači:

Cyklus s continue
index.html

Skript spočíta všetky správne zadané čísla. Hodnoty NaN, ktoré parseInt() vracia v prípade, že sa parsovanie nepodarilo, sa preskočí. Namiesto continue by sme samozrejme mohli použiť len blok else, kód by sme tým však zbytočne zaboril (príklad pozri použitie else namiesto return; nižšie).

Návestí (label)

Pomocou návestí si môžeme v JavaScripte pomenovať cyklus. Toho môžeme využiť napr. Ak budeme chcieť pri použití vnorených cyklov ukončiť vnútri vnútorného cyklu cyklus vonkajšie.

Syntax návestí je pomerne mätúce a v praxi je možné opäť elegantne obísť pomocou return za predpokladu, že je kód vo funkcii. Odporúčam je skôr nepoužívať.

Príklad

Ukážme si ako návestí vyzerajú. Predpokladajme, že máme nejakú databázu zamestnancov a budeme chcieť nájsť zamestnanca zodpovedného za dané oddelenie. Naši databázu tu bude predstavovať pole polí. Každý zamestnanec bude mať meno a zoznam (pole) oddelenie, za ktoré je zodpovedný:

let zamestnanci = [
    [ 'Jan Novotný', [ 'pečivo', 'nápoje' ] ],
    [ 'Karel Starý', [ 'elektro', 'domáce potreby' ] ],
    [ 'Pavla Nováková', [ 'mrazené výrobky', 'mliečne výrobky' ] ]
];

Ak budeme hľadať napr. Zamestnanca 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 a tak ukončíme vonkajšie cyklus z cyklu vnútorného.

Ukážka použitia návestí:

cyklusZamestnanci: for (let zamestnanec of zamestnanci) {
    for (let oddeleni of zamestnanec[1]) {
        document.write('Opakovaní vnútorného cyklu.<br>');
        if (oddeleni === 'elektro') {
            document.write('Oddelenie elektro má na starosti ' + zamestnanec[0] + '.<br>');
            break cyklusZamestnanci;
        }
    }
}

Ukážka v prehliadači:

Cyklus s návestidlom a break
index.html

Ukážka by fungovala aj bez použitia návestí, 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.

Môžete si príklad vyskúšať aj bez návestí a tiež bez príkazu break.

Návestí sa príliš často nepoužíva a je možné sa bez neho zaobísť, tu ho uvádzame skôr pre úplnosť.

Príklad s return

Lepším riešením než použiť návestí je opäť kód vložiť do funkcie a tú pri nájdení zamestnanca ukončiť cez 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 zamestnanca vyhľadávať.

Príklad s totožným výstupom bude s funkciou a return vyzerať nasledovne:

let zamestnanci = [
    [ 'Jan Novotný', [ 'pečivo', 'nápoje' ] ],
    [ 'Karel Starý', [ 'elektro', 'domáce potreby' ] ],
    [ 'Pavla Nováková', [ 'mrazené výrobky', 'mliečne výrobky' ] ]
];

function najdiZamestnancePodleOddeleni(hledaneOddeleni) {
    for (let zamestnanec of zamestnanci) {
        for (let oddeleni of zamestnanec[1]) {
            document.write('Opakovaní vnútorného cyklu<br>');
            if (oddeleni === hledaneOddeleni) {
                return zamestnanec[0];
            }
        }
    }
}

let hledanyZamestnanec = najdiZamestnancePodleOddeleni('elektro');
document.write('Oddelenie elektro má na starosti ' + hledanyZamestnanec + '<br>');

výsledok:

Tvoja stránka
localhost

return;

Keď sme už zavítali k return, povedzme si, že je dokonca možné zavolať return 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šie.

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é:

tlacitko.onclick = function() {
    let a = parseInt(cislo1.value);
    let b = parseInt(cislo2.value);
    if (Number.isNan(a) || Number.isNan(b)) {
        alert('Zadajte celé čísla!');
        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ľa odsadený a neprehľadný:

tlacitko.onclick = function() {
    let a = parseInt(cislo1.value);
    let b = parseInt(cislo2.value);
    if (Number.isNan(a) || Number.isNan(b))
        alert('Zadajte celé čísla!');
    else {
        alert(a + b);
        // ...
    }
};

Pozor! Ak funkcia vracia nejakú hodnotu, nikdy v nej nevolajte len 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:

Cyklus for
index.html

Písať logiku priebehu behu cyklu aj logiku v cykle na jeden riadok však nie je intuitívne. Navyše sa tak môže ľahko zabudnúť na inkrementácia premenné alebo ju inkrementovať viackrát.

Dokonca nie je nutné v hlavičke cykle for uvádzať akýkoľvek príkaz:

for (;;) {
    // nekonečný cyklus
}

Tento zápis je rovnaký ako:

while (true) {
    // nekonečný cyklus
}

Pozor: oba príklady povedú k zaseknutiu vlákna danej záložky prehliadača!

Oba vyššie deklarované cykly beží do nekonečna a môžete 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 ale nie je priamo v deklarácii cykle, je pomerne neprehľadné zistiť kedy cyklus vôbec skončí a ľahké v urobiť z takéhoto cyklu nechtiac nekonečný, zvlášť, keď z neho vyskakuje viac podmienkami a neskrývane všetky možné prípady.

V budúcej lekcii, Textové reťazce v JavaScripte do tretice - Split a join , si ukážeme, že string vie predsa len ešte niečo navyše. Prezradím, že budeme dekódovať Morseovu abecedu.


 

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é 38x (3.52 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Podmienky v JavaScripte druhýkrát
Všetky články v sekcii
Základné konštrukcie jazyka JavaScript
Preskočiť článok
(neodporúčame)
Textové reťazce v JavaScripte do tretice - Split a join
Článok pre vás napísal Roman
Avatar
Užívateľské hodnotenie:
3 hlasov
Roman
Aktivity