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: zasayHi
nejsou žádné závorky. Kdyby tam byly, pak byfunc = sayHi()
zapsal dofunc
vý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:
question
Text otázkyyes
Funkce, která se spustí, pokud je odpověď „Ano“no
Funkce, 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.
.