Výrazy funkcí

V JavaScriptu není funkce „magickou jazykovou strukturou“, ale zvláštním druhem hodnoty.

Syntaxe, kterou jsme použili dříve, se nazývá Deklarace funkce :

function sayHi() {
 alert( "Hello" );
}

Existuje další syntaxe pro vytvoření funkce, která se nazývá Výraz funkce .

Umožňuje nám vytvořit novou funkci uprostřed jakéhokoli výrazu.

Například:

let sayHi = function() {
 alert( "Hello" );
};

Zde můžeme vidět proměnnou sayHi získání hodnoty, nové funkce, vytvořené jako function() { alert("Hello"); } .

Protože k vytvoření funkce dochází v kontextu výrazu přiřazení (napravo od = ), toto je Výraz funkce .

Upozorňujeme, že za function není žádné jméno klíčové slovo. Vynechání názvu je u funkčních výrazů povoleno.

Zde jej ihned přiřadíme do proměnné, takže význam těchto ukázek kódu je stejný:"vytvoř funkci a vlož ji do proměnné sayHi ".

."

V pokročilejších situacích, na které narazíme později, může být funkce vytvořena a okamžitě volána nebo naplánována na pozdější spuštění, nikde se neukládá, takže zůstává anonymní.

Funkce je hodnota

Zopakujme:bez ohledu na to, jak je funkce vytvořena, funkce je hodnota. Oba výše uvedené příklady ukládají funkci do sayHi proměnná.

Tuto hodnotu můžeme dokonce vytisknout pomocí alert :

function sayHi() {
 alert( "Hello" );
}

alert( sayHi ); // shows the function code

Upozorňujeme, že na posledním řádku se funkce nespustí, protože za sayHi nejsou žádné 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 nakládat jako s hodnotou. Výše uvedený kód ukazuje jeho řetězcovou reprezentaci, což je zdrojový kód.

Funkce je jistě speciální hodnota v tom smyslu, že ji můžeme nazvat jako sayHi() .

Ale pořád je to hodnota. Můžeme s tím tedy pracovat jako s jinými druhy hodnot.

Funkci můžeme zkopírovat do jiné proměnné:

function sayHi() { // (1) create
 alert( "Hello" );
}

let func = sayHi; // (2) copy

func(); // Hello // (3) run the copy (it works)!
sayHi(); // Hello // this still works too (why wouldn't it)

Zde je podrobný popis toho, co se děje výše:

  1. Deklarace funkce (1) vytvoří funkci a vloží ji do proměnné s názvem sayHi .
  2. Řádek (2) zkopíruje do proměnné func . Znovu si všimněte:za sayHi nejsou žádné závorky . Pokud ano, pak func = sayHi() by napsal výsledek hovoru sayHi() do func , nikoli funkci sayHi sám.
  3. Nyní lze funkci volat jako sayHi() a func() .

Mohli jsme také použít funkční výraz k deklaraci sayHi , v prvním řádku:

let sayHi = function() { // (1) create
 alert( "Hello" );
};

let func = sayHi;
// ...

Všechno by fungovalo stejně.

Proč je na konci středník?

Možná se divíte, proč mají funkční výrazy středník ; na konci, ale deklarace funkcí nikoli:

function sayHi() {
 // ...
}

let sayHi = function() {
 // ...
};

Odpověď je jednoduchá:Funkční výraz je zde vytvořen jako function(…) {…} uvnitř příkazu přiřazení:let sayHi = …; . Středník ; je doporučeno na konci příkazu, není součástí syntaxe funkce.

Středník by tam byl pro jednodušší přiřazení, například let sayHi = 5; a je zde také pro přiřazení funkcí.

Funkce zpětného volání

Podívejme se na další příklady předávání funkcí jako hodnot a používání výrazů funkcí.

Napíšeme funkci ask(question, yes, no) se třemi parametry:

question
Text otázky
yes
Funkce se spustí, pokud je odpověď „Ano“
no
Funkce se spustí, pokud je odpověď „Ne“

Funkce by se měla zeptat na question a v závislosti na odpovědi uživatele zavolejte na 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 ask
ask("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ý příklad je, že funkce v reálném životě 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 je jiný příběh.

Argumenty showOk a showCancel z ask se nazývají funkce zpětného volání nebo jen zpětná volání .

Myšlenka je taková, že předáme funkci a očekáváme, že bude později v případě potřeby „zavolána zpět“. V našem případě showOk se stane zpětným voláním pro odpověď „ano“ a showCancel pro odpověď „ne“.

K zápisu ekvivalentní kratší funkce můžeme použít Function Expressions:

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ř ask(...) volání. Nemají žádné jméno, a proto se jim říká anonymní . Takové funkce nejsou přístupné mimo ask (protože nejsou přiřazeny k proměnným), ale to je přesně to, co zde chceme.

Takový kód se v našich skriptech objevuje velmi přirozeně, je to v duchu JavaScriptu.

Funkce je hodnota představující „akci“

Běžné hodnoty, jako jsou řetězce nebo čísla, představují data .

Funkci lze vnímat jako akci .

Můžeme jej předávat mezi proměnnými a spouštět, kdy chceme.

Výraz funkce vs deklarace funkce

Pojďme formulovat klíčové rozdíly mezi deklaracemi funkcí a výrazy.

Nejprve syntaxe:jak je v kódu rozlišit.

  • Deklarace funkce: funkce deklarovaná jako samostatný příkaz v toku hlavního kódu:

    // Function Declaration
    function sum(a, b) {
     return a + b;
    }
  • Výraz funkce: funkce vytvořená uvnitř výrazu nebo uvnitř jiného syntaktického konstruktu. Zde je funkce vytvořena na pravé straně „výrazu přiřazení“ = :

    // Function Expression
    let sum = function(a, b) {
     return a + b;
    };

Jemnější rozdíl je kdy funkce je vytvořena enginem JavaScript.

Funkční výraz se vytvoří, když ho vykonání dosáhne, a je použitelný teprve od tohoto okamžiku.

Jakmile tok provádění přejde na pravou stranu přiřazení let sum = function… – tady to je, funkce je vytvořena a může být od této chvíle používána (přidělována, volána atd.).

Deklarace funkcí se liší.

Deklarace funkce může být volána 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 způsobeno vnitřními algoritmy. Když se JavaScript připravuje na spuštění skriptu, nejprve v něm vyhledá globální deklarace funkcí a vytvoří funkce. Můžeme to považovat za „inicializační fázi“.

A po zpracování všech deklarací funkcí se kód provede. Má tedy přístup k těmto funkcím.

Například toto funguje:

sayHi("John"); // Hello, John

function sayHi(name) {
 alert( `Hello, ${name}` );
}

Deklarace funkce sayHi je vytvořen, když se JavaScript připravuje na spuštění skriptu a je viditelný všude v něm.

…Pokud by 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ž je provádění dosáhne. To by se stalo pouze v řádku (*) . Příliš pozdě.

Další speciální vlastností deklarací funkcí je jejich blokový rozsah.

V přísném režimu, když je deklarace funkce v bloku kódu, je viditelná všude uvnitř tohoto bloku. Ale ne mimo ni.

Představme si například, že potřebujeme deklarovat funkci welcome() v závislosti na age proměnnou, kterou získáme za běhu. A pak to plánujeme použít o něco později.

Pokud použijeme deklaraci funkce, nebude fungovat tak, jak má:

let age = prompt("What is your age?", 18);

// conditionally declare a function
if (age < 18) {

 function welcome() {
 alert("Hello!");
 }

} else {

 function welcome() {
 alert("Greetings!");
 }

}

// ...use it later
welcome(); // Error: welcome is not defined

Je to proto, že deklarace funkce je viditelná pouze uvnitř bloku kódu, ve kterém je umístěna.

Zde je další příklad:

let age = 16; // take 16 as an example

if (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, abychom vytvořili welcome viditelné mimo if ?

Správným přístupem by bylo použít funkční výraz a přiřadit welcome na proměnnou, která je deklarována 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 to mohli ještě více zjednodušit pomocí operátoru s otazníkem ? :

let age = prompt("What is your age?", 18);

let welcome = (age < 18) ?
 function() { alert("Hello!"); } :
 function() { alert("Greetings!"); };

welcome(); // ok now
Kdy zvolit deklaraci funkce versus výraz funkce?

Obecně platí, že když potřebujeme deklarovat funkci, první věcí, kterou je třeba zvážit, je syntaxe deklarace funkce. Poskytuje větší svobodu v tom, jak organizovat náš kód, protože takové funkce můžeme volat ještě předtím, než jsou deklarovány.

To je také lepší pro čitelnost, protože je snazší vyhledat function f(…) {…} v kódu než let f = function(…) {…}; . Deklarace funkcí jsou „poutavější“.

…Pokud nám ale deklarace funkce z nějakého důvodu nevyhovuje nebo potřebujeme podmíněnou deklaraci (právě jsme viděli příklad), pak by se měl použít Function Expression.

Shrnutí

  • Funkce jsou hodnoty. Mohou být přiřazeny, kopírovány nebo deklarovány na libovolném místě kódu.
  • Pokud je funkce deklarována jako samostatný příkaz v hlavním toku kódu, nazývá se to „Deklarace funkce“.
  • Pokud je funkce vytvořena jako součást výrazu, nazývá se „Výraz funkce“.
  • Deklarace funkcí jsou zpracovány před provedením bloku kódu. Jsou vidět všude v bloku.
  • Funkční výrazy se vytvoří, když je dosáhne 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 je obvykle čitelnější.

Funkční výraz bychom tedy měli používat pouze tehdy, když se deklarace funkce pro daný úkol nehodí. V této kapitole jsme viděli několik takových příkladů a v budoucnu uvidíme další.