Do této chvíli jsme ohledně jazyka JavaScript učili mnoho a mnoho nových věcí. Věcí, které často potřebují čas na strávení a zažití aby se v hlavě dobře usadily na ta správná místa. Pokud se něco nového a náročného snažíme naučit příliš rychle, snadno se stane, že nám v hlavách nové pojmy lítají jak splašené a není jasné, co souvisí s čím a co kam patří. V této lekci tedy vrhneme více světla na věci, které jste už v minulých lekcích použili, ale možná ještě nebyl čas se nad nimi pořádně zamyslet.

Hodnoty null a undefined

Občas se nám stane, že si potřebujeme nějakou proměnnou připravit, ale zatím ještě nevíme, jaká v ní má být hodnota. Chceme tedy, aby na začátku byla prázdná. To můžeme zařídit pomocí speciální hodnoty null. Toto je v postatě nový typ hodnoty vedle čísel, řetězců, funkcí apod. Můžeme si představit, že hodnota null znamená nic.

'use strict';

const submitClick = () => {
  const passwordElm = document.querySelector('#pass-input');
  const password = passwordElm.value;
  let message = null;

  if (password === 'swordfish') {
    message = 'Access granted';
  } else {
    message = 'Access denied';
  }

  alert(message);
};

const submitBtn = document.querySelector('#submit-btn');
submitBtn.addEventListener('click', submitClick);

Explicitnímu ukládání hodnoty null do proměnných jako výše, bychom se měli spíše vyhýbat. Uvedený program se dá bez problému přepsat bez použití null.

let message = 'Access denied';

if (password === 'swordfish') {
  message = 'Access granted';
}

alert(message);

Často se však stane, že hodnotu null vrací nějaké funkce v situaci, kdy se něco nepovedlo. Velmi častý případ je to u funkce document.querySelector, která vrací null, pokud se jí na stránce nezdaří najít element podle zadaného selektoru.

Pojďme zkusit omylem vybrat vstupní políčko pomocí CSS třídy, která však v HTML vůbec není.

> const passwordElm = document.querySelector('.pass-input')
> passwordElm
null

Vidíte, že v proměnné passwordElm máme místo očekávaného elementu uloženo null.

Otestovat proměnnou na hodnotu null můžeme provést jednoduchou podmínkou.

if (passwordElm === null) {
  console.log('Element nenalezen');
}

Hodnota undefined

Kromě celkem užitečné hodnoty null JavaScript také obsahuje zákeřnou hodnotu undefined. Tato hodnota v podstatě znamená “ještě větší prázdno než nic”. Pokud bychom přirovnali proměnné k šuplíkům, mohli bychom si představovat, že hodnota null znamená prázdný šuplík. Hodnota undefined by pak znamenala, že ve skříni chybí i sám šuplík a zíráme jen na prázdnou díru ve skříni.

Hodnotu undefined potkáme v mnoha situacích, ale nejčastěji ve chvíli, kdy se snažíme přistoupit k vlastnosi, která neexistuje. Je například velmi snadné udělat překlep v anglickém slově length.

> const name = 'martin'
> name.lenght
undefined

Všimněte si, že JavaScript runtime vrací undefined také jako výsledek vytvoření proměnné. Kód uvedený výše tak ve skutečnosti vypadá v konzoli takto.

> const name = 'martin'
undefined
> name.lenght
undefined

Hodnotu undefined najdeme také v proměnných, do kterých nepřiřadíme žádnout hodnotu. Toto je však možné provést pouze s proměnnými vytvořenými pomocí let.

> let name
undefined
> name
undefined

Podobně jako u hodnoty null můžeme přítomnost hodnoty undefined ověřit podmínkou.

if (name === undefined) {
  console.log('Něco se pokazilo');
}

Hodnota undefined nám v budoucní způsobí ještě hodně nepříjemností, je tedy dobré se již teď obrnit trpělivostí.

Procvičování funkcí

1

Pozdravy

pohodička

Představte si, že tvoříte aplikaci na odesílání e-mailů. Každý e-mail je třeba zakončit zdvořilým pozdravem.

  1. Napište funkci bez parametrů s názvem goodbye. Funkce do konzole vypíše rozloučení. Příklad použití:
    > goodbye()
    Nashledanou
    
  2. Končit e-mail slovy “nashledanou” je nezdvořilé. Přidejte proto do funkce goodbye parametr představující jméno pisatele e-mailu. Funkce do konzole vypíše koncový pozdrav i se jménem. Příklad použití:
    > goodbye('Pavel Ovesný')
    S pozdravem Pavel Ovesný
    
  3. Upravte funkci goodbye tak, aby místo vypisování do konzole vrátila pozdrav jako řetězec. Příklad:
    > goodbye('Pavel Ovesný')
    'S pozdravem Pavel Ovesný'
    
2

E-mail

to dáš

Stáhněte si základ stránky, která zobrazuje jeden e-mail.

  1. Napište funkci fillSubject s jedním parametrem subject. Tato funkce ze stránky vybere DOM element představující předmět e-mailu a nastaví jeho obsah na řetězec, který přišel v parametru. Zavolejte funkci z konzole a vykoušejte si nastavit předmět e-mailu na různé řetězce.
  2. Napište funkci fillBody s jedním parametrem body, která ze stránky vybere DOM element představující tělo e-mailu a nastaví jeho obsah dle hodnoty parametru. Funkci vyzkoušejte v konzoli.
  3. Zkopírujte si do programu funkci goodbye z předchozího cvičení. Do funkce fillBody přidejte další parametr s názvem name. Tento parametr bude představovat jméno odesílatele. Funkce vyplní tělo emailu a na konec přidá pozdrav, který vyrobí pomocí volání funkce goodbye.
3

Převod měny

to dáš

Napište funkci convertToCZK, která převede částku zadanou v cízí měně na české koruny. Funkce bude podporovat následující měny a kurzy.

Měna Kód Kurz
Euro EUR 27.015
Britská libra GBP 29.615
Americký dolar USD 23.197

Výslednou částku zakrouhlete na celé koruny. Příklad použití:

> convertToCZK(25, 'EUR')
675

Pokud funkce jako parametr dostane neznámý kód měny, vrátí jako výsledek null. Otestujte funkci v konzoli.

Obor platnosti proměnných

Mějme následující podmínku, která kontroluje věk uživatele a vypisuje neurvalé hlášky.

if (age < 18) {
  const remains = 18 - age;

  if (remains <= 2) {
    alert('Už to máš za pár');
  } else if (remains <= 5) {
    alert(`Ještě si počkáš ${remains} let`);
  } else {
    alert('Utíkej za mamkou');
  }
} else {
  alert('Vítej mezi dospěláky');
}

Zatím nebudeme řešit odkud se vzala proměnná age. Především si všimneme, že celý program obsahuje dohromady pět různých bloků kódu oddělených složenými závorkami. Pokud uvnitř nějakého bloku vytvoříme proměnnou, například remains, tato proměnná je “vidět” pouze uvnitř tohoto bloku. Tento blok se stává jejím oborem platnosti scope . Jakmile její blok kódu skončí, proměnná remains zanikne a již s ní není možné pracovat.

Pokud se proměnnou pokusíme použít mimo její obor platnosti, JavaScript runtime se bude tvářit jako kdyby tuto proměnnou nikdy neviděl.

if (age < 18) {
  const remains = 18 - age;

  if (remains >= 2) {
    alert('Už to máš za pár');
  } else if (remains >= 5) {
    alert(`Ještě si počkáš ${remains} let`);
  } else {
    alert('Utíkej za mamkou');
  }
} else {
  console.log(remains); // Zde vznikne chyba
  alert('Vítej mezi dospěláky');
}

console.log(remains); // Zde vznikne chyba

Naopak všechny bloky zanořené uvnitř bloku, ve kterém byla proměnná vytvořene, k této proměnné přistupovat mohou. To můžeme v našem kódu vidět v bloku else if, kde proměnnou remains normálně používáme, přestože je vytvořena o blok výše.

Pokud tedy JavaScript runtime narazí uvnitř nějakého bloku na něco, co vypadá jako jméno proměnné, zkusí tuto proměnnou najít uvnitř tohoto bloku. Pokud se mu to nezdaří, podívá se do bloku a patro výš. Takto postupně prochází všechny nadřezené bloky, dokud nenarazí na nejvyšší patro – takzvaný globální obor platnosti global scope .

Globální obor platnosti

Každý JavaScriptový program si můžeme představeit jako jeden velký blok kódu, který v sobě obsahuje všechny příkazy. Takto vznikne globální obor platnosti, ve kterém JavaScript runtime nakonec hledá všechny proměnné, které nanašel nikde jinde. Ukažme si náš program kontrolující věk v celé své kráse.

const age = Number(prompt('Zadej svůj věk:'));

if (age < 18) {
  const remains = 18 - age;

  if (remains >= 2) {
    alert('Už to máš za pár');
  } else if (remains >= 5) {
    console.log(age); // V pořádku
    alert(`Ještě si počkáš ${remains} let`);
  } else {
    alert('Utíkej za mamkou');
  }
} else {
  console.log(age); // V pořádku
  alert('Vítej mezi dospěláky');
}

console.log(age); // V pořádku

V tomto programu vidíme, že proměnná age je vytvořená v globálním oboru platnosti. Takové proměnné říkáme prostě globální. Globální proměnné jsou vidět v celém programu a můžeme je tedy použít kdekoliv. Pokud proměnná není globální a je tedy vytvořena uvnitř nějakého bloku, říkáme o ni, že je lokální local .

Obory platnosti nám pomáhají rodělit náš kód na menší samostatné celky, které se navzájem neovlivňují. Můžete tak bez problému mít ve dvou blocích stejně pojmenovavnou lokální proměnnou a význam bude zcela jasný.

const age = Number(prompt('Zadej svůj věk:'));

if (age < 18) {
  const message = 'Utíkej za mamkou';
  alert(message);
} else {
  const message = 'Vítej mezi dospěláky';
  alert(message);
}

V tom příkladu máme dvě lokální proměnné message, které náhodou mají stejné jméno, jinak však spolu nemají nic společného.

Zastiňování proměnných

Uvažujíc nad příkladem výše vás možná napadne, co by se stalo, kdybychom proměnné message vytvořili takto.

const age = Number(prompt('Zadej svůj věk:'));
const message = 'Utíkej za mamkou';

if (age < 18) {
  alert(message);
} else {
  const message = 'Vítej mezi dospěláky';
  alert(message);
}

Pravidlo při hledání proměnných říká, že se použije ta deklarace, na kterou runtime při procházení nadřazených bloků narazí nejdříve. Díky tomu, že se prohledává vždy od nejnižšího patra k nejvyššímu, v bloku if narazíme nejdřív na globální proměnnou message. Naopak v bloku else dříve najdeme lokální proměnnou. Tomuto principu se říká zastínění shadowing . Proměnná, která je z hlediska hierarchie bloků níže, takzvaně zastíní stejně pojmenovou proměnnou, která se nachází výše.

V praxi je nejlepší, když má náš program tak dobře pojmenované proměnné, že se nevzájem nazastiňují. Zjednodušujeme tak práci všem čtenářům, kteří tak mají o starost méně při louskání našeho kódu. Rozhodně je ale dobré vědět, že zastínění může nastat a JavaScript runtime se s ním snadno vypořádá.

Obory platnosti a funkce

Jak po předchozích lekcích už všichni víme, bloky kódu se používají také k vytváření funkci. Zde do oborů platnosti vstupuje další hráč, a to jsou parametry funkce. Ty se z hlediska hierarchie nacházejí jakoby na rozhraní mezi blokem funkce a jeho nadřazeným blokem. Prohlédněte si porozně následující kód.

'use strict';

const message = 'Vítej ve světě slasti';

const checkAge = (age, message) => {
  if (age < 18) {
    return message;
  } else {
    const message = 'Vítej mezi dospěláky';
    return message;
  }
};

Vytváříme zde funkci checkAge, která má dva parametry age a message. Uvnitř této funkce parametr message zastíní globální proměnnou message. V bloku else je však tento parametr dále zastíněn lokální proměnnou message. Zkuste si rozmyslet, co pak bude výsledkem těchto volání.

> checkAge(15, 'Utři si sopel')
?
> checkAge(21, 'Oh yeah!')
?

Je dobré připomenout, že program výše je napsán obzvlášť zlovolně je zde především ze vzdělávacích důvdodů. Pokud takový kód někady napíšete v praxi, dostanete od vašich kolegů nejspíš pořádně za uši. Nikdo nechce číst kód, nad kterým musí zbytečně hodinu přemýšlet.

Cvičení - porozumění kódu

4

Porozumění kódu

to dáš

Přečtěte si následující úryvky kódu a u každého řekněte, co program vypíše do konzole aniž abyste program spouštěli.

Úryvek 1:

const name = 'Mississippi';

if (name.length > 5) {
  const name = 'Missi';
  console.log(name);
}

console.log(name);

Úryvek 2:

const name = 'Franta';

const greet = (name) => {
  const name = 'Pepa';
  console.log(name);
  return 'Kuba';
};

console.log(greet('Jožin'));

Úryvek 3:

const age = 25;

if (age > 21) {
  const price = 100;
} else if (age > 15) {
  const price = 50;
} else {
  const price = 0;
}

console.log(price);

Doporučené úložky na doma

5

Výplata

to dáš

Vytvořte funkci salary se třemi parametry

  • wage - hrubá mzda v korunách za hodinu
  • hours - kolik hodin denně průměrně precujete
  • days - kolik dní v měsící průměrně pracujete

Funkce spočítá vaši hrubou měsíční mzdu v celých korunách.

Dále vytvořte funkci taxed, která na vstupu obdrží částku a procento zdanění, a vrátí částku zdaněnou podle zadaných procent.

Použítím funkcí salary a taxed spočítejte svoji měsíční mzdu po 15% zdanění.

6

Kalkulačka

to dáš

Představte si úplně obyčejnou kalkulačku pouze s tlačítky pro čísla, čtyřmi základními operacemi a tlačítkem pro rovná se. Pokud na takové kalkulačce chcete spočítat něco velmi jednoduchého, například 2 + 3, musíme stisknout tlačíko 2, poté +, pak 3 a pak =. Kalkulačka tedy nespočítá náš výsledek ve chvíli, kdy mačkáme +, ale až ve chvíli, kdy mačkáme =. Musí si tedy zapamatovat, co jsme namačkali, a všecho spočítat až ve chvíli, kdy stiskneme =.

Napište funkci calc se třemi parametry num1, op a num2, které představují první zadané číslo, zadanou operaci jako řetězec a druhé zadané číslo. Operace může být '+', '-', '*' nebo '/'. Funkce vrátí výsledek výpočtu pro zadanou operaci.

Příklad použití

> calc(2, '+', 3)
5
> calc(3, '*', 7)
21
> calc(10, '/', 4)
2.5
7

Cenník

zapni hlavu

Stáhněte si základ stránky, která nabízí předplatné za nějaké služby. Může jít například o internetovou televizi, pravidelné dovážky jídla nebo třeba webový hosting.

  1. Napište funkci selectPlan s jedním parametrem planNumber. Tento parametr bude představovat číslo plánu. Funkce podle čísla plánu vybere ze stránky správný DOM element a přídá k němu CSS třídu plan--selected. Vyzkoušejte vaši funkci v konzoli s různými čísly.
  2. Opakovaným voláním funkce selectPlan lze na stránce postupně vybrat všechny plány. My bychom však chtěli, aby mohl být vybrát vždy nejvýš jeden. Upravte funkci selectPlan tak, že vybere plán zadaný v parametru a u ostatních plánů výběr zruší. Ke zrušení výběru stačí z pčíslušného prvku odebrat třídu plan--selected.

Povinné čtení na doma - interpolace řetězců

Když chceme v JavaScriptu vytvořit nějaký kus textu, například nějakou zprávu pro uživatele, často potřebujeme do tohoto textu vložit obsah několika různých proměnných.

'Zákazník ' + name + ' utratil ' + amount + ' kč za ' + product + '.';

Abychom se ze všeho toho sčítání a uvozovek nezbláznili, moderní JavaScript nabízí nový zápis řetězců, kterému se odborně říká interpolace řetězců undefined . V tomto zápisu se místo obyčejný nebo dvojitých uvozovek používají takzvaný zpětný apostrof `. Pomocí tohoto zápisu můžeme obsah proměnných vložit do řetězce pomocí znaku $ a složených závorek.

`Zákazník ${name} utratil ${amount} kč za ${product}.`;

Uvnitř složených závorek se nám otevírá jakési JavaScriptové okno, do kterého můžeme vepsat nejen proměnnou, ale zcela libovolný výraz, jehož výsledek bude automaticky zkonvertován na řetězec.

`Zákazník ${name} utratil ${String(amount).padStart()} kč za ${product}.`;

Víceřádkové řetězce a escape sekvence

Dále v tomto kurzu budeme chtít pracovat s delšími řetězci, které se nám často nevejdou na jeden řádek. V JavaScriptu bohužel nelze udělat běžný řetězec na více řádků. Následující kód by bohužel nefungoval.

const name = '
  petr
';

Abychom takový řetězec sestavili, musíme si pomoct sčítáním řetězců.

const may = (
  'Late evening, on the first of May—\n' +
  'The twilit May—the time of love.\n' +
  'Meltingly called the turtle-dove,\n' +
  'Where rich and sweet pinewoods lay.\n' +
  'Whispered of love the mosses frail,\n' +
  'The flowering tree as sweetly lied,\n' +
  'The rose\'s fragrant sigh replied\n' +
  'To love-songs of the nightingale' +
);

Všimněte si použití zpětného lomítka u sybmolů jako \n a \'. Toto jsou takzvané escape sekvence, které nám umožní vložit dovnitř řetězce znak, který by jinak nešel napsat. Sekvence \n znamená nový řádek, sekvence \' vloží apostrof. Ten bychom to řetězce napsat nemohli, protože by si JavaScript myslel, že tím chceme řetězec ukončit.

Naustálé sčítání a vkládání escape sekvencí nás ovšem brzy začne dost zdržovat. Nyní však můžeme použí nové cool řetězce se zpětnými apostrofy a zapsat náš řetězec takto.

const may = `
  Late evening, on the first of May—
  The twilit May—the time of love.
  Meltingly called the turtle-dove,
  Where rich and sweet pinewoods lay.
  Whispered of love the mosses frail,
  The flowering tree as sweetly lied,
  The rose's fragrant sigh replied
  To love-songs of the nightingale
`;

Zde už apostrof i nový řádek můžeme napsat rovnou bez escape sekvencí.

Volitelné úložky na doma

8

Výplňořez

zapni hlavu
  1. Napište funkci fillcut, která jako svůj první parametr str očekává řetězec a jako druhý parametr len kladné celé číslo. Úkolem funkci je oříznout nebo prodloužit zadaný řetězec tak, aby měl délku přesně len.
    • Pokud je vstupní řetězec delší než len, tak funkce odřízne jeho konec a vrátí výsledek.
    • Pokud je vstupní řetězec kratší než len, tak jej doplní od začátku znakem tečky a vrátí výsledek.
    • Pokud je vstupní řetězec dlouhý přesně len, funkce jej vrátí beze změny.

Příklad použití:

> fillcut('petr', 8)
'....petr'
> fillcut('petr', 3)
'pet'
> fillcut('petr', 4)
'petr'
9

Přestupný rok jako funkce

to dáš

Napište funkci isLeapYear, která jako svůj parametr obdrží celé číslo představující rok. Funkce vrátí true, pokud je zadaný rok přestupný. V opačném případě vrátí false.