Parametri JavaScript funkcije
Ovde ćemo obraditi neke malo naprednije teme vezane za parametre funkcije. Ako mislite da se nećete baš snaći sa ovim tekstom, preporučujemo vam da obnovite lekciju o funkcijama.
Broj parametara
Funkcije se obično deklarišu sa listom formalnih parametara, a pozivaju navođenjem liste stvarnih parametara.
function fun(formalni parametri) {...}
...
fun(stvarni parametri)
U klasičnim programskim jezicima važi pravilo da se parametri moraju podudarati po broju, tipu i značenju. Ono što vas može iznenaditi je da JavaScript neće praviti nikakav problem ako se broj stvarnih ne poklopi sa brojem formalnih parametara.
Ako prosledimo više parametara nego što se očekuje, parametri viška se prosto ignorišu.
Ako prosledimo manje parametara nego što se očekuje, nedostajući parametri će u funkciji biti nedefinisani, tj. imaće specijalnu vrednost undefined.
Primer za različit broj parametara
Prvo pogledajmo "lakši" slučaj, kada zadamo više parametara nego što treba.
function fun(a,b)
{
return a + b;
}
var x = fun(5, 6, 10); // x = 11
Funkcija očekuje dva parametra, ali pri pozivu funkcije se prosleđuju tri. U ovoj situaciji se poslednji parametar (broj 10) prosto ignoriše.
Drugi slučaj, kada zadamo manje parametara nego što treba bi zaista i mogao da nas dovede do greške u programu.
function fun(a,b)
{
if (b) // isto kao: if (b != undefined)
return a + b;
return a+1;
}
var x = fun(5); // x = 6
Funkcija očekuje dva parametra, ali joj se prosleđuje samo jedan. U tom slučaju će vrednost parametra b biti undefined. Ako postoji mogućnost da se funkciji prosledi manje parametara, onda unutar same funkcije moramo da ispitamo da li parametri postoje. Ako to ne bismo uradili, ova funkcija bi vratila vrednost NaN (Not a Number).
Lista parametara
Jedna od najznačajnijih stvari koje neki programski jezik može da nam pruži, je mogućnost da funkcija primi varijabilan broj parametara. Pošto smo videli da JavaScript ne pravi probleme oko broja prosleđenih parametara, bilo je logično da tvorci jezika to omoguće.
Funkcije u JavaScriptu imaju specijalnu numerisanu listu koja obuhvata sve prosleđene parametre. Ovo je implicitno definisan niz arguments. Znači, bez obzira kako smo deklarisali funkciju, unutar nje možemo pristupati parametrima kao da su elementi niza.
Broj parametara saznajemo pomoću svojstva arguments.length.
Primer za listu parametara
U ovom primeru deklarišemo funkciju bez formalnih parametara, međutim, videćemo kako funkcija računa zbir zadatih brojeva.
function zbir()
{
var r = 0;
for (var i=0; i<arguments.length; i++) {
r += arguments[i];
}
return r;
}
var x = zbir(5, 6, 10); // x = 21
Funkcija nema ni jedan parametar, ali pri pozivu funkcije se prosleđuju tri, a moglo bi koliko želimo. Funkcija preuzima na sebe da obradi zadate parametre. Koristimo for ciklus, da prođemo kroz sve elemente "niza" arguments, koristeći njegovo svojstvo length.
Svaki od zadatih parametara "hvatamo" kao i-ti elemenat ovog "niza".
Prenos parametara po referenci
Kada prosleđujemo parametre, objekti se prosleđuju po referenci, dok se primitivne vrednosti uvek prosleđuju po vrednosti. U velikom broju programskih jezika pri deklarisanju funkcije može se odrediti način na koji će parametri biti prosleđivani. Međutim, JavaScript ne poznaje ovu mogućnost.
Naravno, podrazumeva se da su stvarni parametri koji se prenose po referenci uvek promenljive. Logično, pošto je nemoguće menjati literal (konstantu) ili izraz.
Znamo da se objekti uvek prenose po referencama - nema nam druge, već da simuliramo ovu mogućnost. Ako imamo promenljivu primitivnog tipa, koju želimo da prosledimo po referenci, moramo je "umotati" u objekat, a u ovom slučaju, najpraktičnije u niz.
Primer za parametre po referenci
Hajde prvo da vidimo gde nastaje problem.
function zbir(a,b)
{
a++;
b++;
return a+b;
}
var x = 5;
var y = 10;
var rez = zbir(x,y);
console.log(rez, x, y); // 17 5 10
Funkciji zbir() prosleđujemo dva numerička parametra. Njihove vrednosti uvećavamo za po jedan, i onda vraćamo njihov zbir. Zaista, za vrednosti 5 i 10 dobijamo rezultat 17. Šta god da smo unutar funkcije redili sa parametrima - ima isto dejstvo kao da su u pitanju lokalne promenljive.
To je zato što su stvarni parametri x i y (pošto sadrže primitivne, numeričke, vrednosti) prosleđeni po vrednosti. Tako se promena na a i b ne odražava na x i y, koji i dalje imaju vrednosti 5 i 10.
Međutim, šta ako nam baš zatreba da se promena nastala unutar funkcije vidi i na stvarnim parametrima? Pa, umesto običnih promenljivih, radićemo sa nizovima koji imaju po jedan element.
function zbir(a,b)
{
a[0]++;
b[0]++;
return a[0]+b[0];
}
var x = [5];
var y = [10];
var rez = zbir(x, y);
console.log(rez, x[0], y[0]); // 17 6 11
Prikazano rešenje nije baš elegantno, ali, što bi rekli, "završava posao". Više nemamo promenljive x i y - to su sada nizovi. Naravno, i funkcija mora biti drugačija - promenili smo je da radi sa "nultim" elementima niza (to su jedini elementi u nizovima, pošto sadrže samo po jednu vrednost).
Pošto su nizovi u stvari objekti, prenose se po referenci, što znači da šta god radimo sa elementima niza unutar funkcije, biće "primenjeno" i van funkcije.
Imenovani parametri
Imenovani parametri su prilično koristan jezički konstrukt, kojim je moguće tačno odrediti koji parametri se prosleđuju funkciji a koji ne, posebno u slučajevima kada funkcija dozvoljava mogućnost da ne budu svi parametri prosleđeni. Konkretno, neki jezici dozvoljavaju da "propustimo" parametre:
fun(vred, , vred...)
Neki drugi jezici, pak, omogućavaju da tačno navedemo koje parametre prosleđujemo:
function fun(param, param...) {...}
...
fun(param=vred, param=vred...)
Ni jedna od ovih korisnih stvari ne postoje u JavaScriptu. Znači da opet moramo da se dovijamo. Jedan od načina je prosleđivanje vrednosti null za parametre koji nedostaju.
Drugi način bi bilo korišćenju objekata. Umesto da funkcija prima celu listu parametara, imaće samo jedan parametar - objekat. U ovom slučaju, svojstva objekta "glume" različite parametre. Kada budemo prosleđivali vednosti, navešćemo samo ona svojstva objekta koja želimo.
Primer za imenovane parametre
Prvo da vidimo kako bismo se snašli korišćenjem vrednosti null.
function broj(znak, vrednost)
{
if (znak) {
if (znak == "-")
return -vrednost;
}
return vrednost;
}
var a = broj("-", 153); // a = -153
var b = broj(null, 346); // b = 346
Želimo da napravimo funkciju kojoj prosleđujemo znak u obliku stringa i broj. Funkcija na osnovu zadatog znaka vraća odgovarajuću vrednost broja. E sad, želimo da ako ne navedemo znak, bude podrazumevan "+". Tako u funkciji ispitujemo da li je znak definisan, tj. da li se "čita" kao neka vrednost koja se razlikuje od 0, null, undefined, false.
Evo sada drugog pristupa, u kome simuliramo imenovane parametre.
function broj(p)
{
if (p.znak) {
if (p.znak == "-")
return -p.vrednost;
}
return p.vrednost;
}
var a = broj({vrednost:153, znak:"-"}); // a = -153
var b = broj({vrednost:346}); // b = 346
Dakle, funkcija ima samo jedan parametar p, ali je to u stvari objekat koji "krije" u sebi sve potrebne parametre u obliku svojstava.
Glavna razlika je u načinu kako pozivamo funkciju. Navodimo objektnu konstantu, tj. objekat koji pravimo tu na licu mesta i to sa svojstvima koja predstavljaju parametre.
Dodatna pogodnost je što parametre (svojstva) ne moramo da navodimo u bilo kakvom posebnom redosledu.
- J. Resig, B. Bibeault (2013): Secrets of the JavaScript Ninja, Manning Publications, New York
- A. Rauschmayer (2014): Speaking JavaScript, O’Reilly, Sebastopol