034.3 Lecke 2
Tanúsítvány: |
Web Development Essentials |
---|---|
Verzió: |
1.0 |
Témakör: |
034 JavaScript programozás |
Fejezet: |
034.3 JavaScript vezérlési struktúrák és funkciók |
Lecke: |
2/2 |
Bevezetés
A JavaScript nyelv által biztosított, standard beépített függvények mellett a fejlesztők saját, egyéni függvényeket is írhatnak, hogy a bemenetet az alkalmazás igényeinek megfelelő kimenethez rendeljék. Az egyéni függvények alapvetően egy kifejezés részeként máshol felhasználható, egységbezárt utasításkészletet jelentenek.
A függvények használatával elkerülhetjük a kódduplikátumok írását, mivel a függvények a program különböző pontjairól hívhatók. Ezenkívül ha az utasításokat függvényekbe csoportosítjuk, azzal megkönnyítjük az egyéni műveletek eseményekhez való kötését, ami a JavaScript-programozás egyik központi szempontja.
Függvény definiálása
Ahogy egy program mérete nő, függvények használata nélkül egyre nehezebb lesz megszervezni, hogy mit csinál. Minden függvény saját privát változó hatókörrel rendelkezik, így a függvényen belül definiált változók csak ugyanazon függvényen belül lesznek elérhetőek, így nem keverednek össze más függvények változóival. A globális változók továbbra is elérhetők a függvényeken belülről, de a bemeneti értékek függvénybe küldésének előnyösebb módja a függvényparaméterek használata. Példaként az előző leckében bemutatott prímszám-meghatározó programra fogunk építeni:
// Egy egyszerű prímszám-tesztelő
// A kiértékelni kívánt szám
let candidate = 231;
// Segédváltozó
let is_prime = true;
// Kezdjük az 1 utáni legkisebb prímszámmal
let factor = 2;
// Folyamatos értékelés, amíg a factor kisebb, mint a candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// A maradék nulla, tehát a candidate nem prímszám.
is_prime = false;
break;
}
// A következő szám, amivel osztjuk a candidate számot
factor++;
}
// Írjuk ki az eredményt a konzolra
if ( is_prime )
{
console.log(candidate, "pírm");
}
else
{
console.log(candidate, "nem prím");
}
Ha később a kódban ellenőrizni kell, hogy egy szám prím-e, akkor meg kell ismételni a már megírt kódot. Ez a gyakorlat nem ajánlott, mert az eredeti kód bármilyen javítását vagy fejlesztését kézzel kellene megismételni mindenhol, ahová a kódot bemásolták. Ráadásul a kód megismétlése megterheli a böngészőt és a hálózatot, és lelassíthatja a weboldal megjelenítését. Ehelyett helyezzük át az érintett utasításokat egy függvénybe:
// Egy egyszerű prímszám-tesztelő függvény
function test_prime(candidate)
{
// Segédváltozó
let is_prime = true;
// Kezdjük az 1 utáni legkisebb prímszámmal
let factor = 2;
// Folyamatos értékelés, amíg a factor kisebb, mint a candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// A maradék nulla, tehát a candidate nem prímszám
is_prime = false;
break;
}
// A következő szám, amivel osztjuk a candidate számot
factor++;
}
// Az eredmény visszaadása
return is_prime;
}
A függvénydeklaráció a function
utasítással kezdődik, amelyet a függvény neve és paraméterei követnek. A függvény nevének ugyanazokat a szabályokat kell követnie, mint a változók nevének. A függvény paramétereit, más néven a függvény argumentumait vessző választja el, és zárójelben vannak.
Tip
|
Az argumentumok felsorolása a függvény deklarációjában nem kötelező. A függvénynek átadott argumentumok a függvényen belül egy tömbszerű |
A példában a test_prime
függvénynek csak egy argumentuma van: a candidate
argumentum, amely a tesztelendő prímszám-jelölt. A függvény argumentumai úgy működnek, mint a változók, de értéküket a függvényt hívó utasítás adja meg. Például a test_prime(231)
utasítás meghívja a test_prime
függvényt, és a 231-es értéket rendeli a candidate
argumentumhoz, amely ezután a függvény törzsén belül elérhető lesz, mint egy közönséges változó.
Ha a hívó utasítás egyszerű változókat használ a függvény paramétereihez, azok értékei átmásolódnak a függvény argumentumaiba. Ezt az eljárást - a hívó utasításban használt paraméterek értékeinek átmásolását a függvényen belül használt paraméterekbe - hívjuk argumentumok érték szerinti átadásának. A függvény által az argumentumon végzett bármilyen módosítás nem érinti a hívó utasításban használt eredeti változót. Ha azonban a hívó utasítás összetett objektumokat használ argumentumként (azaz olyan objektumot, amelyhez tulajdonságok és metódusok kapcsolódnak) a függvény paramétereihez, akkor azok referenciaként kerülnek átadásra, és a függvény képes lesz módosítani a hívó utasításban használt eredeti objektumot.
Az értékként átadott argumentumok, valamint a függvényen belül deklarált változók nem láthatók a függvényen kívül, vagyis hatókörük annak a függvénynek a törzsére korlátozódik, ahol deklarálták őket. Ennek ellenére a függvényeket általában arra használjuk, hogy a függvényen kívül látható kimenetet hozzunk létre. Ahhoz, hogy egy értéket megosszunk a hívó függvénnyel, a függvény definiál egy return
utasítást.
Például az előző példában a test_prime
függvény az is_prime
változó értékét adja vissza. Ezért a függvény bárhol helyettesítheti a változót, ahol azt az eredeti példában használnánk:
// A kiértékelni kívánt szám
let candidate = 231;
// Írjuk ki az eredményt a konzolra
if ( test_prime(candidate) )
{
console.log(candidate, "prím");
}
else
{
console.log(candidate, "nem prím");
}
A return
(visszaadás) utasítás, ahogy a neve is mutatja, visszaadja a vezérlést a hívó függvénynek. Ezért bárhol is van a függvényben a return
utasítás, az azt követő rész nem hajtódik végre. Egy függvény több return
utasítást is tartalmazhat. Ez akkor lehet hasznos, ha például feltételes utasításblokkokon belül vannak, így a függvény minden egyes futtatáskor vagy végrehajtja, vagy nem hajtja végre az adott return
utasítást.
Egyes függvények nem adnak vissza értéket, éppen ezért a return
utasítás nem kötelező. A függvény belső utasításai a jelenlététől függetlenül végrehajtódnak, így a függvények például globális változók értékének vagy hivatkozással átadott objektumok tartalmának megváltoztatására is használhatók. Ettől függetlenül, ha a függvény nem rendelkezik return
utasítással, akkor a függvény alapértelmezett visszatérési értéke undefined
lesz: ez egy olyan lefoglalt változó, amelynek nincs értéke, és nem írható.
Függvény-kifejezések
A JavaScriptben a függvény egy más típusú objektum, így a függvények a változókhoz hasonlóan használhatók a szkriptben. Ez a tulajdonság akkor válik egyértelművé, ha a függvényt egy alternatív szintaxissal, az úgynevezett függvény-kifejezéssel deklaráljuk:
let test_prime = function(candidate)
{
// Segédváltozó
let is_prime = true;
// Kezdjük az 1 utáni legkisebb prímszámmal
let factor = 2;
// Folyamatos értékelés, amíg a factor kisebb, mint a candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// A maradék nulla, tehát a candidate nem prímszám
is_prime = false;
break;
}
// A következő szám, amivel osztjuk a candidate számot
factor++;
}
// Az eredmény visszaadása
return is_prime;
}
Az egyetlen különbség a mostani példa és a korábban látott példa függvénydeklarációja között az első sorban van: let test_prime = function(candidate)
van a function test_prime(candidate)
helyett. A függvénykifejezésben a test_prime
nevet a függvényt tartalmazó objektumra használjuk, és nem magára a függvényre. A függvénykifejezésekben definiált függvények ugyanúgy hívhatók, mint a deklarációs szintaxissal definiált függvények. Míg azonban a deklarált függvények a deklarációjuk előtt és után, a függvénykifejezések csak az inicializálásuk után hívhatók. A változókhoz hasonlóan a kifejezésben definiált függvény inicializálás előtti meghívása hivatkozási hibát okoz.
Függvény-rekurzió
Az utasítások végrehajtásán és a beépített függvények hívásán kívül az egyéni függvények más egyéni függvényeket is hívhatnak, beleértve saját magukat is. Egy függvény önmagából történő hívását függvény-rekurziónak nevezzük. A megoldandó probléma típusától függően a rekurzív függvények használata egyszerűbb lehet, mint a beágyazott ciklusok használata ismétlődő feladatok elvégzésére.
Eddig megtanultuk azt, hogy hogyan használhatunk függvényt annak tesztelésére, hogy egy adott szám prím-e. Most tegyük fel, hogy egy adott szám után következő prímszámot szeretnénk megtalálni. Használhatunk while
ciklust a jelölt szám növelésére, és írhatunk egy beágyazott függvényt, amely a jelölt szám egész számú tényezőit keresi:
// Ez a függvény visszaadja a következő prímszámot
// az egyetlen argumentumként megadott szám után
function next_prime(from)
{
// Minket csak a pozitív prímek érdekelnek,
// ezért a kettőnél kisebb számok után a
// 2-es számot tekintjük a következő prímszámnak.
if ( from < 2 )
{
return 2;
}
// A 2 is az egyetlen páros pozitív prím,
// így könnyebb lesz külön kezelni.
if ( from == 2 )
{
return 3;
}
// Csükkentsük a "from" értékét, ha páros szám
if ( from % 2 == 0 )
{
from--;
}
// Kezdjünk el 3-nál nagyobb prímeket keresni!
// Az elsőszámú jelölt a következő páratlan szám
let candidate = from + 2;
// A "true" miatt a ciklus addig tart, amíg nem találunk egy prímszámot
while ( true )
{
// Vezérlést segítő segédváltozó
let is_prime = true;
// A "candidate" páratlan szám, szóval a loop
// csak páratlan számokat próbál, a 3-al kezdve
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// A maradék nulla, tehát a candidate nem prím.
// A következő candidate tesztelése
is_prime = false;
break;
}
}
// Ciklus vége és visszatérés a candidate-vel, ha prím
if ( is_prime )
{
return candidate;
}
// Ha még nincs prímtalálat, próbálja meg a következő páratlan számot
candidate = candidate + 2;
}
}
let from = 1024;
console.log("A következő prím a ", from, "után a ", next_prime(from));
Vegyük észre, hogy a while
ciklushoz egy állandó feltételt kell használnunk (a zárójelben lévő true
kifejezést) és az is_prime
segédváltozót, hogy tudjuk, mikor kell megállítani a ciklust. Bár ez a megoldás helyes, az egymásba ágyazott ciklusok használata nem olyan elegáns, mint a rekurzió használata ugyanezen feladat esetén:
// Ez a függvény visszaadja a következő prímszámot
// az egyetlen argumentumként megadott szám után
function next_prime(from)
{
// Minket csak a pozitív prímek érdekelnek,
// ezért a kettőnél kisebb számok után a
// 2-es számot tekintjük a következő prímszámnak.
if ( from < 2 )
{
return 2;
}
// A 2 is az egyetlen páros pozitív prím,
// így könnyebb lesz külön kezelni.
if ( from == 2 )
{
return 3;
}
// Csükkentsük a "from" értékét, ha páros szám
if ( from % 2 == 0 )
{
from--;
}
// Kezdjünk el 3-nál nagyobb prímeket keresni!
// Az elsőszámú jelölt a következő páratlan szám
let candidate = from + 2;
// A "candidate" páratlan szám, szóval a loop
// csak páratlan számokat próbál, a 3-al kezdve
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// A maradék nulla, tehát a candidate nem prím.
// Hívjuk meg a next_prime függvényt rekurzívan, ezúttal
// a bukott candidate-t használva argumentumként.
return next_prime(candidate);
}
}
// A "candidate" nem osztható más egész számmal, mint
// 1-gyel és önmagával, ezért prímszám.
return candidate;
}
let from = 1024;
console.log("A következő prím a ", from, "után a ", next_prime(from));
A next_prime
mindkét verziója a következő prímszámot adja vissza az egyetlen argumentumként megadott szám után (from
). A rekurzív változat az előző változathoz hasonlóan a speciális esetek (azaz a kettővel kisebb vagy egyenlő számok) ellenőrzésével kezdi. Ezután inkrementálja a jelöltet, és a for
ciklussal elkezdi keresni az esetleges egész számú faktorokat (vegyük észre, hogy a while
ciklus már nincs benne). Ekkor az egyetlen páros prímszámot már teszteltük, ezért a jelöltet és a lehetséges faktorokat kettővel növeljük. (Egy páratlan szám plusz kettő a következő páratlan szám).
A példában szereplő for
ciklusból csak kétféleképpen lehet kilépni. Ha az összes lehetséges tényezőt megvizsgáljuk, és egyiknek sem marad nulla maradéka a jelölt osztásakor, akkor a for
ciklus befejeződik, és a függvény a jelöltet adja vissza, mint a from
után következő prímszámot. Ellenkező esetben, ha a factor
a candidate
egész számú tényezője (candidate % factor == 0
), a visszaadott érték a rekurzívan meghívott next_prime
függvényből származik, ezúttal a from
paraméterként megnövelt jelölt
értékkel. A next_prime
hívásai egymásra kerülnek, amíg végül az egyik jelölt nem talál egész faktorokat. Ekkor a prímszámot tartalmazó utolsó next_prime
példány visszaadja azt az előző next_prime
példánynak, és így egymás után végig, egészen az első next_prime
példányig. Annak ellenére, hogy a függvény minden egyes meghívása ugyanazokat a neveket használja a változókhoz, a meghívások egymástól el vannak szigetelve, így a változóik elkülönítve vannak a memóriában.
Gyakorló feladatok
-
Milyen túlterhelést tudnak a fejlesztők csökkenteni a függvények használatával?
-
Mi a különbség az értékkel és a hivatkozással átadott függvényargumentumok között?
-
Melyik érték lesz egy egyéni függvény kimenete, ha nincs visszatérési utasítás?
Gondolkodtató feladatok
-
Mi a legvalószínűbb oka annak, hogy a függvény-kifejezés szintaxissal deklarált függvény meghívásakor Uncaught Reference Error hiba lép fel?
-
Írjunk egy
multiples_of
nevű függvényt, amely három argumentumot kap:faktor
,from
ésto
. A függvényen belül használjuk aconsole.log()
utasítást afactor
minden olyan szorzatának kiírására, amely afrom
ésto
között van!
Összefoglalás
Ez a lecke a saját függvények JavaScript-kódban történő írásával foglalkozik. Az egyéni függvények lehetővé teszik a fejlesztő számára, hogy az alkalmazást újrafelhasználható kód-“darabokra” (chunk) ossza, megkönnyítve ezzel a nagyobb programok írását és karbantartását. Ez a lecke a következő eljárásokat és fogalmakat veszi át:
-
Egyéni függvények definiálása: függvénydeklarációk és függvénykifejezések.
-
Paraméterek használata függvény bemeneteként.
-
A
return
utasítás használata a függvény kimenetének beállításához. -
Függvény-rekurzió.
Válaszok a gyakorló feladatokra
-
Milyen túlterhelést tudnak a fejlesztők csökkenteni a függvények használatával?
A függvények lehetővé teszik a kód újrafelhasználását, ami megkönnyíti a kód karbantartását. A kisebb szkriptfájllal memóriát és letöltési időt is megtakarítunk.
-
Mi a különbség az értékkel és a hivatkozással átadott függvényargumentumok között?
Ha értékkel adjuk át, az argumentumot a függvénybe másoljuk, és a függvény nem tudja módosítani az eredeti változót a hívó utasításban. Ha hivatkozással adjuk át, a függvény képes manipulálni a hívó utasításban használt eredeti változót.
-
Melyik érték lesz egy egyéni függvény kimenete, ha nincs visszatérési utasítás?
A visszatérési érték
undefined
lesz.
Válaszok a gondolkodtató feladatokra
-
Mi a legvalószínűbb oka annak, hogy az expression szintaxissal deklarált függvény meghívásakor Uncaught Reference Error hiba lép fel?
A függvényt a szkriptfájlban való deklarálása előtt hívták meg.
-
Írjunk egy
multiples_of
nevű függvényt, amely három argumentumot kap:faktor
,from
ésto
. A függvényen belül használjuk aconsole.log()
utasítást afactor
minden olyan szorzatának kiírására, amely afrom
ésto
között van!function multiples_of(factor, from, to) { for ( let number = from; number <= to; number++ ) { if ( number % factor == 0 ) { console.log(factor, "*", number / factor, "=", number); } } }