A JavaScriptben a függvény nem egy “mágikus nyelvi struktúra”, hanem egy speciális értéktípus.
A szintaxist, amelyet korábban használtunk, függvénydeklarációnak nevezzük:
function sayHi() { alert( "Hello" );}
A függvény létrehozására létezik egy másik szintaxis is, amelyet függvénykifejezésnek hívunk.
Ez így néz ki:
let sayHi = function() { alert( "Hello" );};
Itt a függvényt explicit módon hozzuk létre és rendeljük a változóhoz, mint bármely más értéket. Nem számít, hogyan definiáljuk a függvényt, az csak egy érték, amelyet a sayHi
változóban tárolunk.
A kódminták jelentése ugyanaz: “hozzunk létre egy függvényt, és tegyük a sayHi
változóba”.
Az alert
segítségével ki is tudjuk nyomtatni ezt az értéket:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Megjegyezzük, hogy az utolsó sorban nem futtatjuk a függvényt, mert a sayHi
után nincsenek zárójelek. Vannak olyan programozási nyelvek, ahol a függvény nevének bármilyen említése a függvény végrehajtását okozza, de a JavaScript nem ilyen.
A JavaScriptben a függvény egy érték, tehát értékként kezelhetjük. A fenti kód a string reprezentációját mutatja, ami a forráskódot jelenti.
Tény, hogy egy függvény egy speciális érték, abban az értelemben, hogy úgy hívhatjuk, mint sayHi()
.
De attól még egy érték. Tehát ugyanúgy dolgozhatunk vele, mint másfajta értékekkel.
Másolhatunk egy függvényt egy másik változóba:
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)
Íme, mi történik fentebb részletesen:
- A függvény deklaráció
(1)
létrehozza a függvényt, és asayHi
nevű változóba teszi. - A
(2)
sor afunc
változóba másolja. Ismét megjegyezzük: asayHi
után nincsenek zárójelek. Ha lennének, akkor afunc = sayHi()
asayHi()
hívás eredményét írná afunc
-be, nem pedig magát asayHi
függvényt. - Most a függvény
sayHi()
ésfunc()
néven is hívható.
Megjegyezzük, hogy a sayHi
deklarálásához használhattunk volna függvénykifejezést is, az első sorban:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Minden ugyanúgy működne.
Felmerülhet a kérdés, hogy a Function Expression végén miért van pontosvessző ;
, de a Function Declaration nem:
function sayHi() { // ...}let sayHi = function() { // ...};
A válasz egyszerű:
- Nincs szükség
;
-re a kódblokkok és az ezeket használó szintaxis szerkezetek végén, mintif { ... }
,for { }
,function f { }
stb. - Egy függvénykifejezést használunk az utasításon belül:
let sayHi = ...;
, mint érték. Ez nem egy kódblokk, hanem egy hozzárendelés. A pontosvessző;
az utasítások végén ajánlott, függetlenül attól, hogy mi az érték. Tehát a pontosvessző itt nem magához a függvénykifejezéshez kapcsolódik, csak lezárja az utasítást.
Callback függvények
Nézzünk további példákat a függvények értékként való átadására és a függvénykifejezések használatára.
Írunk egy ask(question, yes, no)
függvényt három paraméterrel:
question
A kérdés szövege yes
Futtatandó függvény, ha a válasz “Igen” no
Futtatandó függvény, ha a válasz “Nem”
A függvénynek meg kell kérdeznie a question
-et, és a felhasználó válaszától függően meg kell hívnia yes()
vagy 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);
A gyakorlatban az ilyen függvények igen hasznosak. A fő különbség egy valós ask
és a fenti példa között az, hogy a valós funkciók összetettebb módokat használnak a felhasználóval való interakcióra, mint egy egyszerű confirm
. A böngészőben az ilyen függvény általában egy szépen kinéző kérdésablakot rajzol. De ez már egy másik történet.
A ask
showOk
és showCancel
argumentumait callback függvényeknek vagy egyszerűen csak visszahívásoknak hívják.
A lényege, hogy átadunk egy függvényt, és elvárjuk, hogy később “visszahívják”, ha szükséges. Esetünkben a showOk
lesz a visszahívás az “igen” válaszra, és a showCancel
a “nem” válaszra.
Függvénykifejezésekkel ugyanezt a függvényt sokkal rövidebben is megírhatjuk:
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."); });
Itt a függvények közvetlenül a ask(...)
híváson belül vannak deklarálva. Nincs nevük, ezért anonimnak nevezzük őket. Az ilyen függvények nem érhetők el a ask
-en kívül (mert nincsenek változókhoz rendelve), de mi itt éppen ezt akarjuk.
Az ilyen kód nagyon természetesen jelenik meg a szkriptjeinkben, ez a JavaScript szellemében történik.
Reguláris értékek, például karakterláncok vagy számok reprezentálják az adatokat.
A függvényt úgy is felfoghatjuk, mint egy műveletet.
Változók között átadhatjuk, és akkor futtathatjuk, amikor akarjuk.
Function Expression vs Function Declaration
Fogalmazzuk meg a legfontosabb különbségeket a Function Declarations és a Expressions között.
Először a szintaxis: hogyan lehet megkülönböztetni őket a kódban.
-
Funkciódeklaráció: egy függvény, amelyet külön utasításként deklarálunk a fő kódfolyamatban.
// Function Declarationfunction sum(a, b) { return a + b;}
-
Funkciókifejezés: egy függvény, amelyet egy kifejezésen belül vagy egy másik szintaxiskonstrukción belül hozunk létre. Itt a függvény a “hozzárendelési kifejezés” jobb oldalán jön létre
=
:// Function Expressionlet sum = function(a, b) { return a + b;};
A finomabb különbség az, amikor a függvényt a JavaScript motor hozza létre.
A Function Expression akkor jön létre, amikor a végrehajtás eléri, és csak attól a pillanattól használható.
Mihelyt a végrehajtási folyam eljut a hozzárendelés jobb oldalára
let sum = function…
– itt tartunk, a függvény létrejön, és innentől kezdve használható (hozzárendelhető, hívható stb. ).A függvénydeklaráció más.
Egy Function Declaration előbb hívható, mint ahogy definiálták.
Egy globális Function Declaration például az egész scriptben látható, függetlenül attól, hogy hol van.
Ez a belső algoritmusoknak köszönhető. Amikor a JavaScript felkészül a szkript futtatására, először globális Funkciódeklarációkat keres benne, és létrehozza a függvényeket. Ezt tekinthetjük “inicializálási szakasznak”.
Az összes Function Declaration feldolgozása után pedig végrehajtja a kódot. Így hozzáférhet ezekhez a függvényekhez.
Ez például így működik:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
A Function Declaration
sayHi
akkor jön létre, amikor a JavaScript felkészül a script elindítására, és mindenhol látható benne.…Ha ez egy Function Expression lenne, akkor nem működne:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
A Function Expression akkor jön létre, amikor a végrehajtás eléri. Ez csak a
(*)
sorban történne meg. Túl késő.A függvénydeklarációk másik különleges tulajdonsága a blokk hatókörük.
A szigorú módban, ha egy függvénydeklaráció egy kódblokkon belül van, akkor a blokkon belül mindenhol látható. De azon kívül nem.
Elképzeljük például, hogy egy
welcome()
függvényt kell deklarálnunk aage
változótól függően, amelyet futás közben kapunk. És aztán azt tervezzük, hogy valamikor később használni fogjuk.Ha a Function Declarationt használjuk, akkor nem úgy fog működni, ahogyan azt terveztük:
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
Ez azért van, mert egy Function Declaration csak azon a kódblokkon belül látható, amelyben található.
Itt egy másik példa:
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
Mit tehetünk, hogy a
welcome
aif
-on kívül is látható legyen?A helyes megközelítés az lenne, ha Function Expressiont használnánk és a
welcome
-t aif
-on kívül deklarált és megfelelő láthatósággal rendelkező változóhoz rendelnénk.Ez a kód a kívánt módon működik:
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
Vagy még tovább egyszerűsíthetnénk egy kérdőjeles operátor
?
használatával:let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
Mikor válasszuk a Function Declaration versus Function Expression?Ökölszabályként elmondható, hogy amikor egy függvényt kell deklarálnunk, először a Function Declaration szintaxist kell figyelembe vennünk. Ez nagyobb szabadságot ad a kódunk megszervezésében, mert az ilyen függvényeket már a deklarálásuk előtt meg tudjuk hívni.
Az olvashatóság szempontjából is jobb, hiszen a
function f(…) {…}
könnyebben megkereshető a kódban, mint alet f = function(…) {…};
. A függvénydeklarációk “szemet gyönyörködtetőbbek”.…De ha a függvénydeklaráció valamilyen okból nem felel meg nekünk, vagy feltételes deklarációra van szükségünk (az előbb láttunk egy példát), akkor Function Expressiont kell használni.
Summary
- A függvények értékek. Hozzárendelhetők, másolhatók vagy deklarálhatók a kód bármely helyén.
- Ha a függvényt különálló utasításként deklaráljuk a fő kódfolyamatban, azt “függvénydeklarációnak” nevezzük.
- Ha a függvény egy kifejezés részeként jön létre, azt “függvénykifejezésnek” nevezzük.
- A függvénydeklarációkat a kódblokk végrehajtása előtt dolgozzuk fel. A blokkban mindenhol láthatóak.
- A Funkció-kifejezések akkor jönnek létre, amikor a végrehajtási folyam eléri őket.
A legtöbb esetben, amikor egy függvényt kell deklarálnunk, a Funkció-deklaráció előnyösebb, mert az már a deklaráció előtt látható. Ez nagyobb rugalmasságot biztosít számunkra a kódszervezésben, és általában olvashatóbb is.
A Function Expressiont tehát csak akkor használjuk, ha a Function Declaration nem alkalmas a feladatra. Erre már láttunk néhány példát ebben a fejezetben, és a jövőben még több példát fogunk látni.