PomÄrnÄ Äasto potÅebujeme vykonat podobnou akci na mnoha mÃstech ve skriptu.
NapÅÃklad chceme zobrazit pÄknÄ vypadajÃcà zprávu, když se návÅ¡tÄvnÃk pÅihlásÃ, odhlásà a možná i nÄkde jinde.
Funkce jsou hlavnà âstavebnà kamenyâ programu. UmožÅujà volat kód vÃcekrát, aniž by bylo nutné ho psát znova.
Už jsme vidÄli pÅÃklady vestavÄných funkcÃ, napÅ. alert(zpráva), prompt(zpráva, výchozà hodnota) a confirm(otázka). Můžeme si vÅ¡ak vytvoÅit i své vlastnà funkce.
Deklarace funkcÃ
Funkce vytváÅÃme pomocà deklarace funkce.
Deklarujeme ji takto:
function zobrazZprávu() {
alert( 'Ahoj všichni!' );
}
NapÅed je uvedeno klÃÄové slovo function, pak název funkce, potom máme závorky a v nich seznam parametrů (jsou oddÄleny Äárkami, v uvedeném pÅÃkladu je seznam prázdný, jeho pÅÃklady uvidÃme pozdÄji) a nakonec následujà složené závorky, ve kterých je obsažen kód funkce, nazývaný také âtÄlo funkceâ.
function název(parametr1, parametr2, ... parametrN) {
// tÄlo
}
NaÅ¡i novou funkci pak můžeme volat pomocà jejÃho názvu: zobrazZprávu().
NapÅÃklad:
function zobrazZprávu() {
alert( 'Ahoj všichni!' );
}
zobrazZprávu();
zobrazZprávu();
Volánà zobrazZprávu() vykoná kód funkce. Zde tedy uvidÃme tuto zprávu dvakrát.
Tento pÅÃklad jasnÄ demonstruje jeden z hlavnÃch úÄelů funkcÃ: vyhnout se duplikaci kódu.
Jestliže nÄkdy budeme potÅebovat zmÄnit text zprávy nebo způsob, jakým je zobrazována, bude nám staÄit modifikovat kód na jediném mÃstÄ: ve funkci, která zprávu vypisuje.
Lokálnà promÄnné
PromÄnná deklarovaná uvnitÅ funkce je viditelná pouze v této funkci.
PÅÃklad:
function zobrazZprávu() {
let zpráva = "Ahoj, já jsem JavaScript!"; // lokálnà promÄnná
alert( zpráva );
}
zobrazZprávu(); // Ahoj, já jsem JavaScript!
alert( zpráva ); // <-- Chyba! PromÄnná je dostupná jen uvnitÅ funkce
VnÄjšà promÄnné
Funkce může pÅistupovat i k vnÄjÅ¡Ãm promÄnným, napÅÃklad:
let uživatelskéJméno = 'Jan';
function zobrazZprávu() {
let zpráva = 'Ahoj, ' + uživatelskéJméno;
alert(zpráva);
}
zobrazZprávu(); // Ahoj, Jan
Funkce má plný pÅÃstup k vnÄjšà promÄnné a může ji také modifikovat.
PÅÃklad:
let uživatelskéJméno = 'Jan';
function zobrazZprávu() {
uživatelskéJméno = "Pavel"; // (1) zmÄna vnÄjšà promÄnné
let zpráva = 'Ahoj, ' + uživatelskéJméno;
alert(zpráva);
}
alert( uživatelskéJméno ); // Jan pÅed volánÃm funkce
zobrazZprávu();
alert( uživatelskéJméno ); // Pavel, funkce zmÄnila hodnotu promÄnné
VnÄjšà promÄnná se použije jen tehdy, neexistuje-li lokálnÃ.
Pokud je uvnitÅ funkce deklarována promÄnná se stejným názvem, tak zastÃnà tu vnÄjÅ¡Ã. NapÅÃklad v nÞe uvedeném kódu funkce použÃvá lokálnà promÄnnou uživatelskéJméno a vnÄjšà ignoruje:
let uživatelskéJméno = 'Jan';
function zobrazZprávu() {
let uživatelskéJméno = "Pavel"; // deklarace lokálnà promÄnné
let zpráva = 'Ahoj, ' + uživatelskéJméno; // Pavel
alert(zpráva);
}
// funkce vytvoÅà a použije své vlastnà uživatelskéJméno
zobrazZprávu();
alert( uživatelskéJméno ); // Jan, beze zmÄny, funkce nepÅistupovala k vnÄjšà promÄnné
PromÄnné deklarované mimo jakoukoli funkci, napÅÃklad uživatelskéJméno ve výše uvedeném kódu, se nazývajà globálnÃ.
Globálnà promÄnné jsou viditelné z každé funkce (pokud nejsou zastÃnÄny lokálnÃmi).
Je dobrým zvykem minimalizovat použÃvánà globálnÃch promÄnných. Modernà kód obsahuje jen málo globálnÃch promÄnných nebo vůbec žádné. VÄtÅ¡ina promÄnných je uvnitÅ svých funkcÃ. NÄkdy ovÅ¡em mohou být globálnà promÄnné užiteÄné k ukládánà dat na úrovni projektu.
Parametry
Do funkcà můžeme pÅedávat data pomocà parametrů.
V nÞe uvedeném pÅÃkladu má funkce dva parametry: odKoho a text.
function zobrazZprávu(odKoho, text) { // parametry: odKoho, text
alert(odKoho + ': ' + text);
}
zobrazZprávu('Anna', 'Ahoj!'); // Anna: Ahoj! (*)
zobrazZprávu('Anna', "Co se dÄje?"); // Anna: Co se dÄje? (**)
Když se na ÅádcÃch (*) a (**) volá funkce, zadané hodnoty se zkopÃrujà do lokálnÃch promÄnných odKoho a text. Pak je funkce použije.
Dalšà pÅÃklad: máme promÄnnou odKoho a pÅedáme ji funkci. VÅ¡imnÄte si, že funkce zmÄnà hodnotu odKoho, ale tato zmÄna nenà vidÄt zvenÄÃ, jelikož funkce obdržà vždy kopii hodnoty:
function zobrazZprávu(odKoho, text) {
odKoho = '*' + odKoho + '*'; // aby âodKohoâ vypadalo lépe
alert( odKoho + ': ' + text );
}
let odKoho = "Anna";
zobrazZprávu(odKoho, "Ahoj"); // *Anna*: Ahoj
// hodnota âodKohoâ je stejná, funkce zmÄnila lokálnà kopii
alert( odKoho ); // Anna
Když je nÄjaká hodnota pÅedána funkci jako parametr, můžeme ji oznaÄovat jako argument.
Jinými slovy, abychom tyto pojmy upÅesnili:
- Parametr je promÄnná uvedená v závorkách v deklaraci funkce (pojem použÃvaný v okamžiku deklarace).
- Argument je hodnota, která je pÅedána funkci, když je volána (pojem použÃvaný v okamžiku volánÃ).
Když funkce deklarujeme, znaÄÃme jejich parametry. Když funkce voláme, voláme je spolu s argumenty.
Ve výše uvedeném pÅÃkladu bychom mohli ÅÃci: âfunkce zobrazZprávu je deklarována se dvÄma parametry a pak je volána se dvÄma argumenty: odKoho a "Ahoj".â
Výchozà hodnoty
Jestliže je volána funkce, ale nÄkterý argument nenà poskytnut, pak odpovÃdajÃcà hodnota bude undefined.
NapÅÃklad výše uvedenou funkci zobrazZprávu(odKoho, text) lze volat jen s jediným argumentem:
zobrazZprávu("Anna");
To nenà chyba. Takové volánà vypÃÅ¡e "*Anna*: undefined". Protože hodnota text nebyla pÅedána, stane se undefined.
V deklaraci funkce můžeme specifikovat tzv. âvýchozÃâ hodnotu parametru (pokud argument chybÃ) pomocà =:
function zobrazZprávu(odKoho, text = "text nenà zadán") {
alert( odKoho + ": " + text );
}
zobrazZprávu("Anna"); // Anna: text nenà zadán
Jestliže argument k parametru text nebude pÅedán, bude mÃt hodnotu "text nenà zadán".
Výchozà hodnota bude nastavena i tehdy, když parametr existuje, ale je striktnÄ roven undefined, napÅÃklad:
showMessage("Anna", undefined); // Anna: text nenà zadán
Zde "text nenà zadán" je ÅetÄzec, ale může to být složitÄjšà výraz, který se vyhodnotà a pÅiÅadà jen tehdy, jestliže argument chybÃ. Je tedy možné i toto:
function zobrazZprávu(odKoho, text = jináFunkce()) {
// jináFunkce() se vykoná jen tehdy, když argument u parametru text chybÃ
// jejà výsledek pak bude argumentem parametru text
}
V JavaScriptu bude výchozà argument vyhodnocen pokaždé, když bude funkce volána bez pÅÃsluÅ¡ného argumentu.
Ve výše uvedeném pÅÃkladu nebude jináFunkce() vůbec volána, jestliže bude argument u parametru text poskytnut.
Na druhou stranu bude nezávisle na sobÄ volána pokaždé, když bude argument parametru text chybÄt.
PÅed nÄkolika lety JavaScript nepodporoval syntaxi pro výchozà argumenty. Lidé tedy použÃvali jiné způsoby, jak je specifikovat.
V souÄasnosti na nÄ můžeme narazit ve starých skriptech.
NapÅÃklad explicitnà ovÄÅovánà na undefined:
function zobrazZprávu(odKoho, text) {
if (text === undefined) {
text = 'text nenà zadán';
}
alert( odKoho + ": " + text );
}
â¦Nebo pomocà operátoru ||:
function zobrazZprávu(odKoho, text) {
// Je-li hodnota promÄnné text nepravdivá, pÅiÅadÃme výchozà hodnotu
// to pÅedpokládá, že text == "" je totéž jako vůbec žádný text
text = text || 'text nenà zadán';
...
}
Alternativnà výchozà argumenty
NÄkdy má smysl nastavovat výchozà hodnoty argumentů až pozdÄji po deklaraci funkce.
Abychom bÄhem provádÄnà funkce ovÄÅili, zda argument byl pÅedán, můžeme jej porovnat s undefined:
function zobrazZprávu(text) {
// ...
if (text === undefined) { // jestliže argument chybÃ
text = 'prázdná zpráva';
}
alert(text);
}
zobrazZprávu(); // prázdná zpráva
â¦Nebo můžeme použÃt operátor ||:
function zobrazZprávu(text) {
// je-li argument parametru text neuveden nebo je nepravdivý, nastavà se na 'prázdný'
text = text || 'prázdný';
...
}
Modernà enginy JavaScriptu podporujà operátor koalescence ??, který je lepšà použÃt, když by vÄtÅ¡ina nepravdivých hodnot, napÅÃklad 0, mÄla být zpracována ânormálnÄâ:
function zobrazPoÄet(poÄet) {
// je-li poÄet undefined nebo null, zobrazà se "neznámý"
alert(poÄet ?? "neznámý");
}
zobrazPoÄet(0); // 0
zobrazPoÄet(null); // neznámý
zobrazPoÄet(); // neznámý
Návratová hodnota
Funkce může vrátit volajÃcÃmu kódu jako výsledek nÄjakou hodnotu.
NejjednoduššÃm pÅÃkladem je funkce, která seÄte dvÄ hodnoty:
function souÄet(a, b) {
return a + b;
}
let výsledek = souÄet(1, 2);
alert( výsledek ); // 3
Direktiva return může být ve funkci umÃstÄna kdekoli. Když na ni vykonávajÃcà se funkce narazÃ, ukonÄà se a vrátà zadanou hodnotu volajÃcÃmu kódu (v uvedeném pÅÃkladu se pÅiÅadà do promÄnné výsledek).
V jedné funkci se může return vyskytovat mnohokrát. PÅÃklad:
function ovÄÅVÄk(vÄk) {
if (vÄk >= 18) {
return true;
} else {
return confirm('Máš povolenà od rodiÄů?');
}
}
let vÄk = prompt('Kolik je ti let?', 18);
if ( ovÄÅVÄk(vÄk) ) {
alert( 'PÅÃstup povolen' );
} else {
alert( 'PÅÃstup zakázán' );
}
Je možné použÃt return bez hodnoty. To způsobà okamžité ukonÄenà funkce.
PÅÃklad:
function zobrazFilm(vÄk) {
if ( !ovÄÅVÄk(vÄk) ) {
return;
}
alert( "Ukážu vám ten film" ); // (*)
// ...
}
Pokud v uvedeném kódu ovÄÅVÄk(vÄk) vrátà false, pak zobrazFilm nebude pokraÄovat k alert.
return nebo bez nÄj vrátà undefinedJestliže funkce nevrátà žádnou hodnotu, je to stejné, jako by vrátila undefined:
function nedÄlejNic() { /* prázdná funkce */ }
alert( nedÄlejNic() === undefined ); // true
RovnÄž prázdný return je totéž jako return undefined:
function nedÄlejNic() {
return;
}
alert( nedÄlejNic() === undefined ); // true
return a hodnotuJe-li za return dlouhý výraz, může to svádÄt k jeho umÃstÄnà na samostatný Åádek, napÅÃklad:
return
(nÄjaký + dlouhý + výraz + nebo + co * f(a) + f(b))
To vÅ¡ak nebude fungovat, neboÅ¥ JavaScript za return vložà stÅednÃk. Bude to tedy stejné jako:
return;
(nÄjaký + dlouhý + výraz + nebo + co * f(a) + f(b))
Z pÅÃkazu se tedy stane prázdný return.
Jestliže chceme rozdÄlit vracený výraz na vÃce Åádků, mÄli bychom jej zahájit na stejném Åádku jako return, nebo tam aspoÅ umÃstit levou závorku:
return (
nÄjaký + dlouhý + výraz
+ nebo +
co * f(a) + f(b)
)
TeÄ to bude fungovat tak, jak oÄekáváme.
Pojmenovánà funkcÃ
Funkce jsou akce, takže jejich názvem bývá obvykle sloveso. MÄlo by být krátké a mÄlo by co nejpÅesnÄji popisovat, co funkce dÄlá, aby ÄlovÄk, který Äte kód, mÄl tuÅ¡enÃ, co funkce provádÃ.
Je Å¡iroce rozÅ¡ÃÅenou praktikou zahájit název funkce slovesným prefixem (pÅedponou), který pÅibližnÄ popisuje akci. Na významu prefixů se celý tým musà dohodnout.
NapÅÃklad funkce, jejichž název zaÄÃná "zobraz" (v angliÄtinÄ "show"), obvykle nÄco zobrazujÃ.
Funkce, které zaÄÃnajÃâ¦
"vraÅ¥â¦"("getâ¦") â vracejà nÄjakou hodnotu,"vypoÄÃtejâ¦"("calcâ¦") â nÄco vypoÄÃtávajÃ,"vytvoÅâ¦"("createâ¦") â nÄco vytváÅejÃ,"ovÄÅâ¦"("checkâ¦") â nÄco ovÄÅujà a vracejà typ boolean, atd.
PÅÃklady takových názvů:
zobrazZprávu(..) // zobrazà zprávu
vraÅ¥VÄk(..) // vrátà vÄk (nÄjak jej zjistÃ)
vypoÄÃtejSouÄet(..) // vypoÄÃtá souÄet a vrátà výsledek
vytvoÅFormuláÅ(..) // vytvoÅà formuláŠ(a obvykle jej vrátÃ)
ovÄÅOprávnÄnÃ(..) // ovÄÅà oprávnÄnÃ, vrátà true nebo false
Když jsou použity prefixy, pohled na název funkce nám umožÅuje lépe porozumÄt, jaký druh Äinnosti funkce provádà a jaký druh hodnoty vracÃ.
Funkce by mÄla provádÄt pÅesnÄ to, co naznaÄuje jejà název, a nic vÃc.
DvÄ nezávislé akce si zpravidla zasluhujà dvÄ funkce, i když jsou obvykle volány spoleÄnÄ (v takovém pÅÃpadÄ můžeme vytvoÅit tÅetà funkci, která bude volat ty dvÄ).
NÄkolik pÅÃkladů poruÅ¡enà tohoto pravidla:
vraÅ¥VÄkâ bylo by Å¡patnÄ, kdyby zobrazovalaalerts vÄkem (mÄla by ho jen vrátit).vytvoÅFormuláÅâ bylo by Å¡patnÄ, kdyby modifikovala dokument a formuláŠdo nÄj pÅidávala (mÄla by ho jen vytvoÅit a vrátit).ovÄÅOprávnÄnÃâ bylo by Å¡patnÄ, kdyby zobrazovala zprávupÅÃstup povolen/zamÃtnut(mÄla by jen ovÄÅit právo pÅÃstupu a vrátit výsledek).
Tyto pÅÃklady pÅedpokládajà bÄžný význam prefixů. Se svým týmem se můžete dohodnout na jiném významu, ale obvykle se pÅÃliÅ¡ neliÅ¡Ã. V každém pÅÃpadÄ byste mÄli jasnÄ rozumÄt tomu, co prefix znamená a co funkce s tÃmto prefixem v názvu může a nemůže dÄlat. Tato pravidla by mÄly dodržovat vÅ¡echny funkce se stejným prefixem a celý tým by je mÄl znát.
Funkce == komentáÅe
Funkce by mÄly být krátké a provádÄt právÄ jednu vÄc. Je-li ta vÄc velká, může se vyplatit rozdÄlit funkci na nÄkolik menÅ¡Ãch funkcÃ. Dodržovat toto pravidlo nemusà být vždy snadné, ale bezpochyby se to vyplatÃ.
OddÄlená funkce se nejenom snadnÄji testuje a ladà â samotná jejà existence je skvÄlým komentáÅem!
NapÅÃklad srovnejte dvÄ nÞe uvedené funkce zobrazPrvoÄÃsla(n). Každá z nich vypÃÅ¡e prvoÄÃsla menšà nebo rovná n.
Prvnà varianta použÃvá návÄÅ¡tÃ:
function zobrazPrvoÄÃsla(n) {
dalÅ¡ÃPrvoÄÃslo: for (let i = 2; i < n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue dalÅ¡ÃPrvoÄÃslo;
}
alert( i ); // prvoÄÃslo
}
}
Druhá varianta použÃvá dalšà funkci jePrvoÄÃslo(n), která otestuje, zda zadané ÄÃslo je prvoÄÃslo:
function zobrazPrvoÄÃsla(n) {
for (let i = 2; i < n; i++) {
if (!jePrvoÄÃslo(i)) continue;
alert(i); // prvoÄÃslo
}
}
function jePrvoÄÃslo(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
Druhou variantu je snadnÄjšà pochopit, že? NamÃsto Äásti kódu vidÃme název akce (jePrvoÄÃslo). Takovému kódu lidé nÄkdy ÅÃkajà sebepopisujÃcÃ.
Funkce tedy můžeme vytvoÅit, i když nemáme v úmyslu je použÃvat opakovanÄ. Strukturujà kód a Äinà jej ÄitelnÄjÅ¡Ãm.
ShrnutÃ
Deklarace funkce vypadá takto:
function název(parametry, oddÄlené, Äárkami) {
/* kód */
}
- Hodnoty pÅedané funkci jako argumenty se zkopÃrujà do lokálnÃch promÄnných.
- Funkce může pÅistupovat k vnÄjÅ¡Ãm promÄnným, ale opaÄnÄ to nefunguje â kód mimo funkci nevidà jejà lokálnà promÄnné.
- Funkce může vracet hodnotu. Pokud žádnou nevrátÃ, jejà výsledek je
undefined.
Aby kód byl Äistšà a srozumitelnÄjÅ¡Ã, doporuÄuje se ve funkcÃch použÃvat zejména lokálnà promÄnné a parametry, ne vnÄjšà promÄnné.
Je vždy jednoduššà pochopit funkci, která obdržà argumenty, pracuje s nimi a vrátà výsledek, než funkci, která neobdržà žádné argumenty, ale jako vedlejšà efekt modifikuje vnÄjšà promÄnné.
Pojmenovánà funkcÃ:
- Název by mÄl jasnÄ popisovat, co funkce dÄlá. Když v kódu vidÃme volánà funkce, dobrý název nám umožÅuje okamžitÄ pochopit, co funkce dÄlá a co vracÃ.
- Funkce je akce, takže názvy funkcà jsou obvykle slovesa.
- Existuje mnoho dobÅe známých prefixů funkcÃ, napÅ.
vytvoÅâ¦,zobrazâ¦,vraÅ¥â¦,ovÄÅâ¦atd. PoužÃvajà se, aby bylo naznaÄeno, co funkce dÄlá.
Funkce jsou hlavnÃmi stavebnÃmi kameny skriptů. Když jsme nynà vysvÄtlili jejich základy, můžeme je zaÄÃt vytváÅet a použÃvat. To je vÅ¡ak teprve zaÄátek. JeÅ¡tÄ mnohokrát se k nim vrátÃme a pronikneme hloubÄji do jejich složitÄjÅ¡Ãch vlastnostÃ.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)