V JavaScriptu není funkce „magickou strukturou jazyka“, ale speciálním druhem hodnoty.
Syntaxe, kterou jsme použili dříve, se nazývá deklarace funkce:
function sayHi() { alert( "Hello" );}
Existuje ještě jiná syntaxe pro vytvoření funkce, která se nazývá funkční výraz.
Vypadá takto:
let sayHi = function() { alert( "Hello" );};
Tady je funkce vytvořena a přiřazena proměnné explicitně, jako každá jiná hodnota. Nezáleží na tom, jak je funkce definována, je to prostě hodnota uložená v proměnné sayHi.
Smysl těchto ukázek kódu je stejný: „vytvoř funkci a vlož ji do proměnné sayHi„.
Tuto hodnotu můžeme dokonce vypsat pomocí alert:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Všimněte si, že poslední řádek funkci nespouští, protože za sayHi nejsou závorky. Existují programovací jazyky, kde jakákoli zmínka o názvu funkce způsobí její spuštění, ale JavaScript takový není.
V JavaScriptu je funkce hodnotou, takže s ní můžeme pracovat jako s hodnotou. Výše uvedený kód ukazuje její řetězcovou reprezentaci, což je zdrojový kód.
Jistě, funkce je zvláštní hodnota v tom smyslu, že ji můžeme volat jako sayHi().
Ale stále je to hodnota. Můžeme s ní tedy pracovat jako s jinými druhy hodnot.
Můžeme funkci zkopírovat do jiné proměnné:
function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)
Tady je podrobně popsáno, co se děje výše:
- Deklarace funkce
(1)vytvoří funkci a vloží ji do proměnné s názvemsayHi. - Řádek
(2)ji zkopíruje do proměnnéfunc. Znovu si všimněte: zasayHinejsou žádné závorky. Kdyby tam byly, pak byfunc = sayHi()zapsal dofuncvýsledek volánísayHi(), nikoliv samotnou funkcisayHi. - Nyní lze funkci volat jako
sayHi()ifunc().
Všimněte si, že jsme mohli k deklaraci sayHi použít také funkční výraz, a to hned v prvním řádku:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Vše by fungovalo stejně.
Mohlo by vás zajímat, proč má funkční výraz na konci středník ;, ale deklarace funkce ne:
function sayHi() { // ...}let sayHi = function() { // ...};
Odpověď je jednoduchá:
- Není třeba
;na konci bloků kódu a syntaktických struktur, které je používají, jakoif { ... },for { },function f { }atd. - Funkční výraz se používá uvnitř příkazu:
let sayHi = ...;, jako hodnota. Nejedná se o blok kódu, ale spíše o přiřazení. Na konci příkazů se doporučuje středník;, ať už je hodnota jakákoli. Středník zde tedy nesouvisí se samotným funkčním výrazem, pouze ukončuje příkaz.
Zpětné funkce
Podívejme se na další příklady předávání funkcí jako hodnot a používání funkčních výrazů.
Napíšeme funkci ask(question, yes, no) se třemi parametry:
questionText otázkyyesFunkce, která se spustí, pokud je odpověď „Ano“noFunkce, která se spustí, pokud je odpověď „Ne“
Funkce by se měla zeptat na question a v závislosti na odpovědi uživatele zavolat yes() nebo no():
function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);
V praxi jsou takové funkce docela užitečné. Hlavní rozdíl mezi skutečným ask a výše uvedeným příkladem spočívá v tom, že skutečné funkce používají složitější způsoby interakce s uživatelem než jednoduchý confirm. V prohlížeči taková funkce obvykle vykreslí pěkně vypadající okno s otázkou. Ale to už je jiný příběh.
Argumenty showOk a showCancel funkce ask se nazývají zpětné volání funkce nebo jen zpětné volání
Jde o to, že předáme funkci a očekáváme, že bude v případě potřeby později „zavolána zpět“. V našem případě se showOk stane zpětným voláním pro odpověď „ano“ a showCancel pro odpověď „ne“.
Pro zápis stejné funkce můžeme použít funkční výrazy mnohem kratší:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });
Zde jsou funkce deklarovány přímo uvnitř volání ask(...). Nemají žádné jméno, a proto se nazývají anonymní. Takové funkce nejsou přístupné mimo ask (protože nejsou přiřazeny proměnným), ale právě to zde chceme.
Takový kód se v našich skriptech objevuje zcela přirozeně, je to v duchu JavaScriptu.
Obvyklé hodnoty jako řetězce nebo čísla představují data.
Funkci můžeme vnímat jako akci.
Můžeme ji předávat mezi proměnnými a spouštět, kdy chceme.
Výraz funkce vs. deklarace funkce
Zformulujme si klíčové rozdíly mezi deklarací a výrazem funkce.
Nejprve syntaxe: jak je v kódu rozlišit.
-
Deklarace funkce: funkce, deklarovaná jako samostatný příkaz v hlavním toku kódu.
// Function Declarationfunction sum(a, b) { return a + b;} -
Vyjádření funkce: funkce, vytvořená uvnitř výrazu nebo uvnitř jiné syntaktické konstrukce. Zde je funkce vytvořena na pravé straně „přiřazovacího výrazu“
=:// Function Expressionlet sum = function(a, b) { return a + b;};
Drobnější rozdíl je v tom, kdy je funkce vytvořena JavaScriptovým enginem:
Funkční výraz je vytvořen v okamžiku, kdy k němu dojde při provádění a je použitelný až od tohoto okamžiku.
Jakmile tok vykonávání přejde na pravou stranu přiřazení let sum = function… – tady už to jde, funkce je vytvořena a od této chvíle ji lze používat (přiřazovat, volat atd. ).
Deklarace funkce se liší.
Deklaraci funkce lze zavolat dříve, než je definována.
Například globální deklarace funkce je viditelná v celém skriptu bez ohledu na to, kde se nachází.
To je dáno vnitřními algoritmy. Když se JavaScript připravuje na spuštění skriptu, nejprve v něm hledá globální Deklarace funkcí a vytváří funkce. Můžeme si to představit jako „inicializační fázi“.
A po zpracování všech Deklarací funkcí se kód spustí. Má tedy k těmto funkcím přístup.
Příklad to funguje takto:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
Deklarace funkcí sayHi se vytvoří, když se JavaScript připravuje na spuštění skriptu, a je v něm všude vidět.
…Kdyby to byl funkční výraz, pak by to nefungovalo:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
Funkční výrazy se vytvářejí, když k nim dojde při provádění. To by se stalo pouze v řádku (*). Příliš pozdě.
Další zvláštností deklarací funkcí je jejich blokový rozsah.
V přísném režimu, když je deklarace funkce uvnitř bloku kódu, je viditelná všude uvnitř tohoto bloku. Ne však mimo něj.
Představme si například, že potřebujeme deklarovat funkci welcome() v závislosti na proměnné age, kterou získáme během běhu. A pak ji plánujeme použít někdy později.
Pokud použijeme deklaraci funkce, nebude fungovat tak, jak má:
let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined
To proto, že deklarace funkce je viditelná pouze uvnitř bloku kódu, ve kterém se nachází.
Tady je další příklad:
let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined
Co můžeme udělat, aby welcome byla viditelná mimo if?
Správný postup by byl použít funkční výraz a přiřadit welcome proměnné, která je deklarovaná mimo if a má správnou viditelnost.
Tento kód funguje tak, jak má:
let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now
Nebo bychom jej mohli ještě více zjednodušit pomocí operátoru otazníku ?:
let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
Pravidlem je, že pokud potřebujeme deklarovat funkci, jako první přichází v úvahu syntaxe Function Declaration. Dává větší volnost v uspořádání našeho kódu, protože takové funkce můžeme volat ještě před jejich deklarací.
To je také lepší pro čitelnost, protože se v kódu lépe hledá function f(…) {…} než let f = function(…) {…};. Deklarace funkcí je více „na očích“.
…Pokud nám však deklarace funkce z nějakého důvodu nevyhovuje nebo potřebujeme podmíněnou deklaraci (právě jsme viděli příklad), pak je třeba použít funkční výraz.
Shrnutí
- Funkce jsou hodnoty. Mohou být přiřazeny, zkopírovány nebo deklarovány na libovolném místě kódu.
- Je-li funkce deklarována jako samostatný příkaz v hlavním toku kódu, nazývá se „Deklarace funkce“.
- Je-li funkce vytvořena jako součást výrazu, nazývá se „Výraz funkce“.
- Deklarace funkce jsou zpracovány před provedením bloku kódu. Jsou viditelné všude v bloku.
- Funkční výrazy se vytvářejí, když k nim dojde tok provádění.
Ve většině případů, kdy potřebujeme deklarovat funkci, je vhodnější Deklarace funkce, protože je viditelná před samotnou deklarací. To nám dává větší flexibilitu v organizaci kódu a obvykle je to čitelnější.
Výraz funkce bychom tedy měli použít pouze tehdy, když se Deklarace funkce pro daný úkol nehodí. V této kapitole jsme viděli několik příkladů a v budoucnu se setkáme s dalšími.
.