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ť:Strany budeme náhodne generovať tak dlho, pokiaľ nebudú splnené vyššie uvedené podmienky. V kóde sa objaví špeciálny znaka + b > c
,a + c > b
ab + c > a
.
`
(tzv.
Tupý prízvuk).
Na slovenskej klávesnici ho môžeme napísať pomocou pravého ALT a písmena ý:
Použijeme taký znak dolára $
. Na klávesnici ho
napíšeme pomocou pravého ALT a písmena ov:
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:
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:
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:
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ý:
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:
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:
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:
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:
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