A JavaScript a C-hez hasonlóan csak egy alprogramot ismer, a függvényt, legalábbis ami a kulcsszót illeti. A függvénynek ugyanis nem
kötelező visszatérési értéket adnunk, és ebben az esetben az alprogram eljárásként működik.
Igazodva a nyelv script jellegéhez, a Javascript tervezői elhagytak mindent a függvénydeklarációból, ami nem feltétlenül szükséges:
nem kell megadnunk visszatérési értéket, sem a paraméterek típusát, csak azok neveit. A deklarációt a function kulcsszó vezeti be. A
visszatérési értéket a return kulcsszóval adhatjuk meg. A függvényeket meghívni a nevükkel, és paramétereikkel lehet.
Például a
szintaktikusan helyes megadás, a függvény pedig jól muködik számokra és stringekre egyaránt:
Értelemszerűen a futtató rendszer semmilyen típusellenőrzést nem végez. Ha nem írunk return utasítást, vagy a return jobb oldalán nem áll kifejezés, a visszatérési érték undefined lesz. Ugyanígy, a hiányzó paraméterek undefined-ek lesznek, ha túl kevés paraméterrel hívjuk meg a függvényt. A túl sok paraméter nem zavarja, a felesleget az arguments tömb segítségével érhetjük el. Ezen a módon változó hosszúságú paraméterlistát is kezelhetünk, de a függvény fejlécében ezt nem tudjuk jelezni. A destruktúrálható értékadás bevezetésével lehetővé vált, hogy a függvényeknek (látszólag) több visszatérési értékük legyen, lásd. destruktúrálható értékadás.
A Javascript 1.8-as verziójától kezdve az egyszerűbb függvények a lambda-kifejezések szintaxisára hasonlító módon is definiálhatóak. Például: az első helyett a második is írható:
A két forma ekvivalens, a második forma csak annyiban különbözik, hogy elhagyásra kerültek a kapcsos zárójelek, és a return utasítás.
A függvények egymásba ágyazása megengedett. Például:
A primitív típusok (number, boolean) érték szerint adódnak át. Az objektumok (ide értve a tömböket, és a függvényeket is), referencia szerint kerülnek átadásra.
Minthogy a paraméter típusa tetszőleges lehet, átadhatunk paraméterként függvényt is, ekkor a függvényre a formális paraméterként megadott nevén hivatkozhatunk. Természetesen tetszőleges számú ilyen függvényparamétert átadhatunk, ügyelnünk kell viszont arra, hogy az aktuális paraméter függvény és a használt formális paraméter függvény paramétereinek száma megegyezzen, hiszen a nyelv ekkor sem ellenőrzi a paraméterek számát, a meg nem adottak undefined értékek lesznek. Például:
Minden függvényhez a kliens létrehoz tehát egy arguments nevű tömböt, amelyet a függvény belsejéből hívhatunk meg, és mely tartalmazza az aktuális paraméterek értékeit. Ez leginkább akkor hasznos, ha nem tudjuk a függvény deklarálásakor, hány paraméterrel kívánjuk a későbbiekben meghívni. Természetesen ennek a tömbnek is használhatjuk a length paraméterét. A JavaScript 1.4-es változata előtt az arguments még a függvény paramétere volt, ezért a meghívása függvénynév.arguments néven történik.
A következő példában több stringet szeretnénk eggyé összekonkatenálni, ám nem ismerjük előre, hogy hány string elemmel kívánjuk meghívni a függvényt, ezért paraméterként csak az elválasztó elemet adjuk át, a stringeket pedig az arguments tömbön keresztül kérdezzük le.
Sokan szerették volna, ha az ECMAScript 4-ben bevezetik a túlterheléses függvényeket,
hogy JavaScriptben is elérhető legyen, de ezt visszautasították. Ezért túlterhelt függvények
bevezetése a Javascriptben továbbra sem lehetséges. Ennek két fő oka van: Egyrészt mivel a
paraméterek típusa nincs feltűntetve a függvénydeklarációban, azok alapján nem
lehet választani. Másrészt, a paraméterek száma szerint sem lehet túlterhelni,
ugyanis egy függvény hívható a specifikáltnál kevesebb paraméterrel (ilyenkor
a hiányzó paraméterek értéke undefined lesz), és több paraméterrel is (ilyenkor
az arguments tömbben érhetőek el a további paraméterek). Ha a szükségesnél kevesebb
paraméterrel hívták a függvényt, a paraméterekhez rendelt alapértelmezett értékek
a következő módon szimulálhatóak:
Természetesen lehetőségünk van rekurzív hívásra, azaz a függvény meghívhatja saját magát is. A tipikus példa erre a faktoriális számolás.
A generátorok a JavaScript 1.7-es verziójában bevezetett, speciális módon használható, általában iteratív számítást végző függvények. A generátorok alkalmazása egy új megközelítése annak a gyakori sémának, hogy egy ciklus minden i. iterációjában meghívunk egy "callback" függvényt. Pl. A Fibonacci számok számítása a JavaScript 1.7 előtti verziók eszközeivel kódolva:
Ugyanezt a feladatot generátor alkalmazásával így is megvalósíthatjuk a nyelv 1.7-es verziója óta:
Egy függvény attól válik generátorrá, hogy szerepel benne a yield utasítás.
A generátorok általában iteratív számítást végeznek, azaz a yield utasítás egy
ciklusmagban található. A generátor
függvényszerű meghívásánál (var g = fib(); a példában) csak a paraméterátadás történik meg
(ha a fib()-nek volnának paraméterei, itt történne meg), de a függvény törzse még nem kerül
végrehajtásra. A függvényszerű meghívás visszatérési értéke egy generátor-iterátor objektum
(a példában g). A generátor iteratív számításának részeredményeit ennek a generátor-iterátornak
a next() műveletével kérdezhetjük le. A next() első hívásának hatására a generátor törzse
elkezd végrehajtódni, egészen az első yield utasításig, ahol az utasítás jobb oldalán álló
értékkel visszatér, ez lesz a next() hívás visszatérési értéke. A generátor ekkor a yield
utasításnál felfüggesztődik, majd a következő next() hívás hatására ettől a ponttól folytatódik
a törzsének végrehajtása. (A yieldre gondolhatunk úgy, mint egy speciális return utasításra,
melynek sajátossága, hogy a következő híváskor nem teljesen elölről kezdődik a törzs végrehajtása,
hanem a legutóbbi ilyen speciális return után.)
Egy generátor tartalmazhat több yield utasítást is, ilyenkor a next() hívás hatására csak
a felfüggesztés helyétől a következő yieldig terjedő rész hajtódik végre. Nem szükségszerű továbbá,
hogy a generátoron belüli iteráció végtelen legyen, lehet véges is, erre azonban a használat során
figyelni kell. Végtelen generátor esetén csak a kliens kódtól függ, hogy meddig akarja használni a
generátort: ha már nincs rá szükség, a close() metódussal zárhatjuk le a generátort. Ekkor a
generátor belsejében lévő esetleges finally blokkok azért végrehajtódnak. Ha viszont a generátor nem
végtelen, akkor a generátorban potenciálisan lévő iteráció ciklusfeltétele hamissá válhat, így
tulajdonképp a generátor "végére érhetünk". Másképp megközelítve: ha a generátor törzsében már
nem kerülhet a vezérlés yield utasításra, a generátor befejeződik. Ha a generátorra befejezett
állapotban hívjuk a next() függvényt, az StopIteration kivételt vált ki. Emiatt egyrészt a
véges iterátorokat érdemes try-catch blokkal védeni, másrészt viszont a kivétel explicit jelzést
jelent az iteráció végére, melyet ki is használhatunk (lásd. pl. lentebb).
Például: egy véges generátor
A generátorokat legpraktikusabban a "for...in" ciklusokban tudjuk használni. Ennek több előnye is van:
a kód nem csak tömörebb, és olvashatóbb lesz, de a next() függvények hívása is automatikus lesz.
Továbbá a "for...in" konstrukció kifejezetten figyeli ill. igényli a StopIteration kivételt, mint
az iteráció végének jelét, vagyis annak kezelésével sem kell foglalkozunk. Például:
Az előző részben ismertetett generátorok sok tulajdonsága következik abból, hogy
iterátorokkal tudjuk őket használni, pl. az, hogy "for...in" ciklusban is használhatunk
generátorokat. A "for...in" ciklust eddig abban a formájában ismertük, hogy tömbökön,
vagy objektumokon iterálhattunk vele. Ilyenkor a háttérben valójában az adott objektum,
ill. tömb iterátorát kérjük el, és azzal végezzük az iterációt. Ezek alapján, pongyolán
fogalmazva, a generátorok segítségével a "bejárható adatszerkezetek" körét a függvényekre
is kiterjeszthetjük.
Bár érdemes ismerni őket, az iterátorokkal a programozónak explicit ritkán kell foglalkoznia,
a "for...in" ciklus szintaktikusan elrejti ezek használatát. Előfordulhat azonban olyan
eset, amikor felül akarjuk definiálni az alapértelmezett iterátort, mely minden objektumhoz
automatikusan létrejön, és annak mezőit sorolja fel. Ekkor az "__iterator__" (2x aláhúzással
az elején, és a végén) függvényt kell felüldefiniálni. Egy objektum iterátorát explicit az
"iterator(<objnev>)" függvénnyel lehet lekérdezni.
Az eval függvény kiértékel egy JavaScript kódot, mely lehet kifejezés, amelyet kiértékel, illetve utasítások sorozata, ekkor azok végrehajtásra kerülnek. Az aritmetikai kifejezésekhez természetesen nem kell használnunk, hiszen azokat a nyelv automatikusan kiértékeli.
Ez a függvény visszaadja egy matematikai kifejezésről, hogy eredménye véges lesz-e. Vagyis hamissal tér vissza, ha a kifejezés eredménye nem szám (NaN), különben igazzal tér vissza. Hamis eredményt hoz például a 0-val való osztás.
Visszaadja, hogy a megadott kifejezés szám-e vagy sem. Itt nem csak számokat, de bármit megadhatunk, és ha a kifejezést nem tudja számmá konvertálni, vagy értelmezhetetlen lesz a szám (például a 0-val való osztás), akkor igazzal tér vissza, különben hamissal.
A parse függvényekkel stringeket tudunk számmá konvertálni. Igaz, a nyelvben van automatikus típuskonverzió,
mégis ha egy szövegbe számon, tizedes ponton, exponensen, illetve előjelen (valamint hexadecimális esetén A-F)
kívűl más is kerül, akkor azt már nem tudja a nyelv konvertálni, viszont a parse függvények feldolgozzák az első
nemkívánatos karakterig a függvényt, és az eddig megkapott számot adják vissza. A parseFloat valós, míg a parseInt
egész számmá próbál konvertálni. A parseInt esetében megadhatunk egy második paramétert is, hogy milyen számrendszerbeli
számmá kívánjuk konvertálni az első paramétert. Ez lehet decimális, oktális és hexadecimális. Ha nem adunk meg paramétert,
akkor decimális számrendszerben dolgozik.
Például:
E két függvény bármely tetszőleges objektumot számmá, illetve szöveggé próbál konverálni, például ha egy dátumot szeretnénk szövegként, illetve számként kiíratni:
Segítségükkel tudjuk kódolni, illetve dekódolni a Unicode szöveget hexadecimális formában. Az escape függvény szövegből kódot állít elő, míg az unescape a kódból állít elő szöveget. Leginkább a serverek használják az URL-ek kezelésére.
A láthatóságnál láttuk, hogy egy függvényen belül deklarált változó hatóköre kiterjed az egész függvényre, függetlenül attól, hogy blokkban van-e, vagy sem. Ez azért van így, mert a JavaScript nem blokkalapú láthatósággal rendelkezik, hanem függvényalapúval. Ez azt jelenti, hogy a JS interpreter a függvényen belül deklarált változókat és függvényeket felülre emeli (Hoisting) és undefined értékkel deklarálja őket, és az aktuális sorban csak értékadás történik.
A változók felemelése a függvény első sorába viszonylag egyszerűen történik. Ha függvény belsejében létrehozunk egy változót, például var i = 0; akkor hoisting esetén a függvény első sorában var i; lesz felvéve, aminek undefined az értéke. Az adott sorban i = 0; értékadás történik, így látható, hogy az első sor után az i változó már látható. Ha egy változó még nem lett deklarálva, de megpróbáljuk kiirítni az értékét, akkor kivétel dobódik, és ha nem kezeljük le, akkor elszáll a program.
Függvényen belül definiált függvények esetén is történik emelés. Mielőtt megnéznénk, tisztáznunk kell, hogy javascriptben többféle függvény deklaráció lehetséges. Az egyik az angolul Function Declaration-nek, vagy magyarul Függvény Deklarációnak nevezett "technika", function függvényNév() { .. }. A másik módszer a Function Expression (Függvény kifejezés), amikor egy hivatkozott névvel (változó) látjuk el a függvényt. var függvényNév = function() { .. }. A két esetben különbözőképp működik az emelés.
Ebben az esetben a függvény törzse is emelődik, így meghívhatjuk a függvényt még azelőtt, hogy azt deklarálnánk és implementálnánk.
Ebben az esetben a változóhoz hasonlóan felemelődik a változó és undefined értékkel deklarálódik, de a függvény törzse nem kerül emelésre, így az előző esettől eltérően most nem tudjuk meghívni az értékadás előtt a függvényt.
Nincs beépített kulcsszó névterekre, helyette csomagoló objektumokat hozhatunk létre var myNameSpace = myNameSpace || {}; formában. Alnévtereket is létre tudunk hozni
mivel egy objektum egy adattagja újabb objektum is lehet. Ezt kihasználva myNameSpace.subNameSpace = {}; alakban felvehetünk alnévtereket.
A with kulcsszóval minősítő nevek nélkül tudjuk használni a névterekben felvett függvényeket. Példa: