2. blokk · Építőkövek · 5. fejezet
A függvény egy névvel ellátott, újrahasználható kódblokk: egyszer megírod, és utána akárhányszor
meghívhatod. Bemenetet (paramétereket) kaphat, és visszaadhat egy eredményt. Ettől a
fejezettől a feladatokban is függvényeket írsz: az ellenőrző meghívja őket, és a return-nel
visszaadott értéket nézi meg.
undefined jön vissza.Képzeld a függvényt egy gépnek: beadsz neki valamit (bemenet), elvégez egy munkát, és kiad valamit (eredmény). A lényeg: egyszer megírod, sokszor használod. Az előző fejezet mintáit (számlálás, összegzés, keresés) eddig minden alkalommal újra le kellett gépelned — mostantól egy-egy függvénybe csomagolod, és csak a nevét hívod.
brutto, kosarOsszeg).A függvényt a function kulcsszóval hozzuk létre (deklaráljuk), és a nevével + zárójellel
hívjuk meg. A deklaráció önmagában nem futtat semmit — a törzs csak híváskor fut le:
// Létrehozás (deklaráció) — itt még nem történik semmi: function koszont() { console.log("Helló!"); } // Hívás — ekkor fut le a törzs: koszont(); // Helló! koszont(); // Helló! (akárhányszor hívható)
A koszont (zárójel nélkül) maga a függvény mint érték; a koszont() (zárójellel) a
meghívása. Gyakori kezdő hiba lehagyni a zárójelet — akkor nem fut le semmi, csak „ránézünk" a függvényre.
Ennek a különbségnek a 7. szakaszban még hasznát vesszük.
A függvény neve ige vagy „mit ad vissza" legyen, camelCase írásmóddal: brutto,
kosarOsszeg, raktaronVan. A feladatokban a nevet az ellenőrző adja meg —
pontosan úgy kell elnevezned, különben nem találja meg.
A függvény bemeneti adatokat kaphat. A definícióban felsorolt neveket paramétereknek, a híváskor ténylegesen átadott értékeket argumentumoknak hívjuk. A paraméter a függvényen belül egy közönséges lokális változó:
function koszont(nev) { // nev = paraméter console.log("Szia, " + nev + "!"); } koszont("Anna"); // "Anna" = argumentum → Szia, Anna! koszont("Béla"); // Szia, Béla! function teglalapTerulet(a, b) { // több paraméter vesszővel console.log(a * b); } teglalapTerulet(3, 5); // 15 — a sorrend számít: a = 3, b = 5
Ha egy argumentumot nem adunk át, a paraméter értéke undefined lesz. Ezt kerüli el az
alapértelmezett érték, amely csak akkor lép életbe, ha az adott helyen nem érkezett argumentum:
function szallitasiDij(osszeg, dij = 990) { if (osszeg >= 20000) { return 0; // 20 000 Ft felett ingyenes } return dij; } szallitasiDij(5000); // 990 (dij alapértelmezett) szallitasiDij(5000, 1490); // 1490 (megadtuk) szallitasiDij(25000); // 0
A JavaScript nem szól: a fölösleges argumentumokat eldobja, a hiányzók undefined lesznek (vagy az
alapértelmezett értéket kapják). Ezért az alapértelmezett paramétereket mindig a lista végére tedd.
A return kulcsszóval a függvény visszaad egy értéket, amellyel a hívás helyén tovább dolgozhatunk:
eltároljuk, kiírjuk, vagy egy másik kifejezésbe tesszük. A függvényhívás így maga is egy kifejezés, amelynek értéke van:
function brutto(netto) { return Math.round(netto * 1.27); // visszaadja az eredményt } let ar = brutto(6990); // 8877 → eltároljuk console.log(brutto(1000) + brutto(2000)); // 3810 → tovább számolunk vele
A console.log csak kiír a konzolra, de nem ad vissza semmit. Ha egy függvényben nincs
return (vagy üres a return;), a hívás értéke undefined.
A feladatok ellenőrzője a visszaadott értéket nézi — ha csak kiírod az eredményt, a teszt elbukik!
function osszead(a, b) { console.log(a + b); } let x = osszead(2, 3); // kiír 5-öt, de… console.log(x); // undefined
function osszead(a, b) { return a + b; } let x = osszead(2, 3); console.log(x * 10); // 50
A return utáni sorok már nem futnak le. Ezt kihasználva a „rossz eseteket" a függvény
elején elintézhetjük egy-egy korai kilépéssel (early return, guard clause) — a fő logika így nem kerül egyre mélyebb
else ágakba:
function kedvezmenyesAr(ar, kupon) { if (kupon !== "NYAR10") { return ar; // nincs érvényes kupon → itt vége } return Math.round(ar * 0.9); // ide csak érvényes kuponnal jutunk el } function prim(n) { if (n < 2) return false; // guard: 0, 1, negatívak for (let i = 2; i < n; i++) { if (n % i === 0) return false; // találtunk osztót → kilépünk a ciklusból ÉS a függvényből } return true; // a ciklus végigfutott: nincs osztó }
Figyeld meg: a prim-ben nem kell a 4. fejezet „kapcsoló" változója és break-je —
a return egyszerre lép ki a ciklusból és a függvényből.
A függvényt nem csak deklarációval hozhatod létre: egy változóba is beteheted. Ez a függvénykifejezés — a függvénynek itt nincs saját neve, a változó neve azonosítja:
const duplaz = function (x) { return x * 2; }; console.log(duplaz(21)); // 42 — ugyanúgy hívjuk
A function deklarációt a JavaScript a fájl elejére „emeli" (hoisting), így már a definíció
előtti sorban is meghívhatod. A const f = function… vagy arrow kifejezés viszont csak a
sora után létezik — előtte hívva hibát kapsz. Jó szokás: a függvényeket előbb definiáld, aztán használd, és akkor nem kell rá gondolnod.
Az arrow function (nyíl-függvény) a függvénykifejezés rövidebb írásmódja. Modern kódban — és a 10. fejezet tömb-metódusainál — ez a legelterjedtebb forma. Két alakja van:
// Blokkos alak: {} és return, mint a hagyományosnál const brutto = (netto) => { const afa = netto * 0.27; return Math.round(netto + afa); }; // Rövid alak: EGYETLEN kifejezés → a {} és a return elhagyható, // a kifejezés értéke automatikusan a visszatérési érték const duplaz = x => x * 2; // egy paraméternél a zárójel is elhagyható const osszead = (a, b) => a + b; // több paraméternél kell a zárójel const udvozlet = () => "Helló!"; // paraméter nélkül: üres zárójel console.log(duplaz(5), osszead(2, 3), udvozlet()); // 10 5 Helló!
x => x * 2 visszaadja az értéket. De ha kapcsos zárójelet nyitsz — x => { x * 2 } —,
az már blokk, és return nélkül undefined-ot ad. Ha objektumot akarsz rövid alakban
visszaadni, tedd zárójelbe: () => ({ nev: "Egér" }).
| Forma | Példa | Mikor? |
|---|---|---|
| Deklaráció | function brutto(n) { … } | „Fő" függvények, amelyeket név szerint hívsz — a feladatokban ezt kérjük |
| Függvénykifejezés | const f = function (n) { … }; | Ritkább; az arrow lényegében kiváltotta |
| Arrow, blokkos | const f = (n) => { … return …; }; | Több lépéses törzs, callbackként átadva |
| Arrow, rövid | const f = n => n * 2; | Egykifejezéses átalakítás, feltétel — tömb-metódusoknál |
A hatókör szabályozza, hol látható egy változó. A let/const változó
abban a blokkban ({ }) él, ahol létrehozták — ez lehet egy függvény törzse, egy
if vagy egy ciklus. Kívülről nem érhető el:
let afakulcs = 1.27; // GLOBÁLIS: mindenhonnan látszik function brutto(netto) { let eredmeny = netto * afakulcs; // LOKÁLIS: csak a függvényben él; afakulcs-ot "látja" kívülről return Math.round(eredmeny); } console.log(brutto(1000)); // 1270 console.log(eredmeny); // HIBA: eredmeny is not defined
if vagy for kapcsos zárójelében létrehozott let is csak ott létezik (a ciklus i-je a ciklus után már nincs).Ha a belső hatókörben ugyanolyan nevű változót hozol létre, mint kint, a belső elfedi a külsőt — a függvényen belül a sajátját látja, a külső érintetlen marad:
let nev = "Globális Géza"; function koszont() { let nev = "Lokális Lujza"; // árnyékolja a külsőt console.log(nev); // Lokális Lujza } koszont(); console.log(nev); // Globális Géza — nem változott
Kerüld, hogy a függvény globális változókat olvasgasson és módosítson. Kapja meg paraméterben, amire szüksége van, és return-nel adja vissza az eredményt. Így kiszámítható, önmagában tesztelhető — és pont ilyen függvényeket kér a gyakorló.
JavaScriptben a függvény ugyanolyan érték, mint egy szám vagy szöveg: változóba tehető (ezt láttuk az arrow-nál), és
átadható egy másik függvénynek argumentumként. Az így átadott függvényt callbacknek hívjuk: „hívj vissza,
amikor kell". Nézd meg, hogyan ír egy saját ismetel függvény, amely valamit n-szer csinál —
de hogy mit, azt a hívó dönti el:
function ismetel(n, fn) { // fn: a callback paraméter for (let i = 1; i <= n; i++) { fn(i); // meghívjuk a kapott függvényt (zárójel!) } } function kiir(szam) { console.log("Kör: " + szam); } ismetel(3, kiir); // átadjuk a kiir függvényt (zárójel NÉLKÜL — nem hívjuk, csak odaadjuk) // Kör: 1, Kör: 2, Kör: 3 ismetel(2, i => console.log(i * 100)); // arrow function a helyszínen: 100, 200
Az ismetel(3, kiir) hívásban a kiir után nincs zárójel: nem az eredményét adjuk át,
hanem magát a függvényt. Az ismetel belsejében aztán fn(i)-vel mi hívjuk meg. Ez a minta a következő
fejezetek alapja: a 10. fejezetben a map, filter, sort mind egy
callbacket vár, és ők döntik el, mikor és mivel hívják.
Az Express útvonalkezelői callback-függvények. Te megírod, mi történjen egy kérésnél, és az Express hívja meg, amikor
befut egy ilyen kérés — pontosan úgy, ahogy az ismetel hívta az fn-t:
app.get('/termekek', (req, res) => { // a 2. argumentum egy arrow function res.json(termekek); // az Express hívja meg, req és res argumentumokkal });
A req (kérés) és res (válasz) a callback paraméterei — ugyanúgy, mint az i az
ismetel-nél. Ha ezt a szakaszt érted, az Express-kurzus első napja már ismerős lesz.
Egy függvény tiszta (pure), ha (1) ugyanarra a bemenetre mindig ugyanazt adja vissza, és (2) nincs mellékhatása: nem módosít külső változót, nem ír a konzolra, nem függ a dátumtól vagy véletlenszámtól. A tiszta függvény könnyen tesztelhető és kiszámítható — a feladatok mind ilyenek.
let osszeg = 0; function hozzaad(ar) { osszeg += ar; // mellékhatás } hozzaad(100); hozzaad(100); // az eredmény attól függ, hányszor hívták
function brutto(netto) { return Math.round(netto * 1.27); } brutto(1000); // 1270 brutto(1000); // 1270 — mindig, bárhol, bárhányszor
Egy függvény meghívhatja saját magát. Ez akkor természetes, ha a feladat egy „kisebb ugyanilyen" feladatra vezethető
vissza. Klasszikus példa a faktoriális: 5! = 5 · 4!, és 4! = 4 · 3!… egészen
1! = 1-ig:
function faktorialis(n) { if (n <= 1) { return 1; // LEÁLLÁSI FELTÉTEL: itt már nem hívjuk újra } return n * faktorialis(n - 1); // kisebb bemenettel hívjuk önmagát } console.log(faktorialis(5)); // 5 · 4 · 3 · 2 · 1 = 120 // faktorialis(5) → 5 * faktorialis(4) → 5 * 4 * faktorialis(3) → … → 5 * 4 * 3 * 2 * 1
Rekurziónál mindig legyen egy ág, amely már nem hív újra (itt: n <= 1), és minden hívás
közelítsen felé (itt: n - 1). Különben a hívások egymásra halmozódnak, míg a JavaScript
Maximum call stack size exceeded hibával leáll — ez a rekurzió „végtelen ciklusa". Ugyanezt a faktoriálist
ciklussal is meg lehet írni; a feladatokban bármelyik utat választhatod.
Írj és hívj meg függvényeket! Figyeld meg a return és a console.log különbségét,
próbáld ki a korai kilépést, és adj át függvényt egy másik függvénynek. A kimenet úgy jelenik meg, ahogy Node-ban a
console.log mutatná.
Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓
Új feladattípus mostantól: minden feladatban egy megadott nevű függvényt írsz. Az ellenőrző több
különböző argumentummal meghívja a függvényedet, és a visszaadott értéket (a return-t!)
hasonlítja a várthoz — a console.log-ot nem nézi. A kiinduló kódban ott a függvény váza; a nevét és a
paramétereit hagyd meg, csak a törzsét írd meg. Nyugodtan írhatsz segédfüggvényeket is mellé.
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.
Szinte biztos, hogy hiányzik a return, vagy egy ág (pl. az else) nem ad vissza semmit.
Nézd végig: minden lehetséges úton van-e return? Ciklus után is kell egy — az az eset, amikor a ciklus
„nem talált semmit".
function nev(param) { … } deklarálja, nev(arg) hívja — a zárójel hív, nélküle csak a függvény mint érték.undefined; alapértelmezett érték: function f(x, y = 0).return visszaadja az értéket és befejezi a függvényt. return nélkül az eredmény undefined. Korai return = guard clause a rossz esetekre.const f = function…) és arrow function: x => x * 2 (rövid, automatikus return) vagy (a, b) => { …; return …; } (blokkos). Csak a deklarációt lehet a definíciója előtt hívni.return-nel.ismetel(3, kiir)). Az Express útvonalkezelői is callbackek.Eddig egy változó egy értéket tárolt. A következő fejezetben a tömbökkel sok értéket kezelünk egyszerre — a webshop terméklistája is egy tömb lesz. A függvényeid tömböt kapnak paraméterként, és ciklussal járják be; a most tanult callback-minta pedig a 10. fejezet tömb-metódusainál tér vissza.