Andrise programmeerimisalane WIKI
Funktsioonid
Funktsioon on alamprogramm, mis tekitab väljakutsekohta tagastatava andmeväärtuse. Funktsioon on korduvkasutatav ehk väljakutsutav erinevatest programmi osadest, vähendades sellega programmi loomise ajakulu, haldamise vaeva ja koodi pikkust. Funktsiooni käivitades peatatakse hetkel aktiivne programmi osa ning antakse järg üle käivitatavale funktsioonile. Kontroll taastub peale käivitatud funktsiooni töö lõpetamist.
JavaScript käsitleb funktsioone kõrgemat-järku objektidena. See tähendab, et funktsioone saab luua programmi töötamise ajal, funktsioone saab sarnaselt teistele väärtustele omistada muutujatele, neid saab edastada teiste funktsioonide parameetritena, saab tagastada mingi tegevuse tulemusena jms. Sellise funktsioonidega opereerimise korral ei ole tegu mitte funktsiooni poolt tagastatava väärtuse omistamise ja liigutamisega, vaid funktsiooniobjekti endaga. Kui funktsioon omistada mõnele muutujale väärtuseks, siis see muutuja muutub ka ise käivitavaks.
Kui lihtsamad objektid on seotud Object.protoype prototüübiga (loe prototüüpidest objektide peatükist), siis funktsioonid on lingitud Function.prototype prototüübiga. Viimane omakorda on aga lingitud Object.prototype prototüübi külge. Seega on ka funktsiooni korral tegu objektiga. Funktsiooni eristab muudest objektidest vaid omadus, et funktsiooni saab käivitada. Käivitamiseks tuleb lisada funktsiooni (võib olla suvaline avaldis, mis tagastab väärtusena funktsiooniobjekti) järele täitmist märkivad sümbolid - sulud.
var ƒ = func. // ƒ on muutuja, mille väärtuseks on objekt - funktsioon - func. ƒ(); // lisades käivitamise sümbolid (), käivitub muutujas ƒ salvestatud funktsioon
Deklareerimine
Funktsiooni saab deklareerida kahel viisil - funktsiooni deklareerimise läbi ning lambda-avaldise kujul funktsiooni literaalina anonüümse funktsiooni omistamisega muutujale.
function nimi ([p1,...[,pn]]) { // Funktsiooni sisu }
[var] nimi = function [nimi] ([p1,...[,pn]]) { // Funktsiooni sisu };
Kuna funktsiooni literaaliga omistatakse väärtus muutujale, oleks korrektne kasutada siin lause lõpetamiseks semikoolonit. Semikooloni kasutamine pole tegelikult kohustuslik, aga kuna tegu pole mitte function a(){…} tüüpi lausega, vaid lausega a = z; kus z on function(){…}, siis erinevad skripti korrektsust kontrollivad vahendid (näiteks JSLint, www.jslint.com) ootavad lause lõpetusena siinkohal semikoolonit.
Funktsiooni defineerimiseks on ka kolmas viis, kasutades funktsiooni Function():
var nimi = Function(["p1",...[,"pn"]], "funktsiooni sisu tekstina");
Näiteks:
var nimi = Function("number","alert(number);"); nimi(123); // 123
Näidatud kolmandat varianti ei ole soovitatav võimalusel mitte kunagi kasutada. Function toetub käsklusele eval, mis tähendab, et selle realiseerimine on suhteliselt aeglane ning mis kõige halvem - seda on praktiliselt võimatu siluda, vead kipuvad aga kergelt tekkima.
Funktsiooni käivitamine
Funktsiooni saab käivitada suluoperaatoriga, lisades operaatori () funktsiooni nime järele. Sulgudes asuvad väärtused edastatakse funktsioonile sisendparameetritena.
alert (123);
Võimalik on käivitada ka anonüümseid funktsioone, mis ei kuulu ühegi muutuja juurde. Selle jaoks tuleb funktsiooni deklaratsioon ümbritseda sulgudega ning sulgude poolt tagastatud väärtust saab omakorda järgmiste sulgudega käivitada - esimesed sulud käituvad sellisel juhul vastavalt kontekstile tagastusoperaatorina, mis tagastavad nende sees oleva avaldise väärtuse (selleks on funktsiooniobjekt) ning teised sulud käituvad käivitusoperaatorina, käivitades operaatorist vasakul asuva funktsiooni.
(function(i){ alert(i); })(123); //123
Sellist anonüümsete funktsioonide käivitamist kasutatakse tavaliselt uue skoobi loomiseks. Kuna skoop JavaScriptis on ainult funktsioonisisene, mitte suvaline {} blokk, võib taoline kunstlik skoobi loomine mõningatel juhtudel - näiteks vältimaks globaalse skoobi „reostamist“ kohalike muutujatega - vajalikuks osutuda.
Sisendparameetrid
Funktsiooni defineerimisel saab määrata ära ka funktsiooni sisendparameetrid, milleks võib olla suvaline arv muutujaid. Formaalsete parameetrite (määratud funktsiooni deklaratsioonis) kasutamine ei ole siiski kohustuslik. Juhul kui on defineeritud näiteks sisendparameeter x, aga seda reaalselt funktsioonile edastatud pole, on funktsiooni skoobis x väärtuseks undefined.
function a(x){ alert(x); } a(); // Väljastab: undefined
Lisaks defineeritule edastatakse funktsioonile veel kaks täiendavat parameetrit - arguments ja this, mis on eritüübilised muutujad. arguments sisaldab massiivi kujul kõiki sisendparameetreid ning this on viide aktiivsele objektile, mille juurde funktsioon kuulub. Täpsemalt saab this kohta lugeda objektide peatükist. Vaikimisi on this väärtuseks globaalne objekt window, objekti meetodi korral aga objekt mille meetodiks funktsioon on.
function a(){ var sum = 0; for(var i=0, item=0; item = arguments[i]; i++){ sum += item; // Liidame kõik sisendparameetrid ükshaaval kokku } alert(sum); } a(1,2,3,4); // 1+2+3+4 = 10
arguments parameeter ei ole tegelikult massiiv, kuigi ta seda paistab. arguments on massiivilaadne objekt. Sellel on olemas küll meetod length massiivi elementide arvu leidmiseks, kuid puuduvad kõik teised massiividega seotud meetodid (push, shift jms.).
var len = arguments.length;
Täiendavalt omab arguments parameeter atribuuti callee, mis tähistab funktsiooni ennast. Kui this viitab hetkel aktiivsele objektile, siis arguments.callee on sünonüüm funktsiooni nimega. Kasutada saab seda rekursiivsete funktsioonide korral.
function a(b, z){ z = z || 1; // juhul kui z väärtus puudub, saab vaikimisi väärtuseks 1 alert(z); if(z<b){ arguments.callee(b, z+1); // käivitame funktsiooni a uue z väärtusega } } a(10); // väljastab ükshaaval numbrid 1-10
Vajadusel saab arguments pseudomassiivi teisendada massiiviks läbi massiivi prototüübi meetodi slice()
var args = Array.prototype.slice.call(arguments);
Sellisel juhul kutsume välja massiivi prototüübis paikneva meetodi, seades funktsiooni meetodiga call hetke objekti viiteks arguments pseudomassiivi. Kuna meetodil slice on sisendparameetrid määramata, kaastakse kõik hetke massiivi elemendid (0 .. arguments.length) ning tagastatakse need uue massiiviobjektina.
Tagastus
Töö lõpetamisel tagastab funktsioon väljakutsekohale väärtuse võtmesõnaga return. Juhul kui funktsioon lõpetab tegevuse enne võtmesõna return ilmnemist või see puudub sootuks, tagastatakse väljakutsekohta väärtus undefined. Erandjuhuks on konstruktor new - kui funktsioon täidetakse koos vastava võtmesõnaga, tagastatakse viit loodud objekti juurde.
Lisaks väärtuse tagastamisele saab võtmesõna return kasutada ka funktsiooni töö sunniviisiliseks lõpetamiseks kuna ühtegi rida peale return-i enam ei täideta.
function a(z){ return z+2; 4+5; // Seda rida ei täideta mitte kunagi }
Skoop
Skoop on programmiosa kehtivuspiirkond milles deklareeritud muutujad omavad väärtust ainult selles piirkonnas - väljaspoolt skoopi muutujate väärtuseid lugeda ega muuta pole võimalik. Erinevalt paljudest teistest C sarnase süntaksiga programmeerimiskeeltest, omavad JavaScriptis skoopi ainult funktsioonid, mitte kõik {…} blokid.
JavaScript käsitleb käivitatud funktsiooni skoobina mitte väljakutsekohta, vaid kohta, kus funktsioon on defineeritud. Üksteise sees defineeritud fuktsioonid moodustavad skoobiahela, milles sisemine skoop saab enda käsutusse kõik välises skoobis kehtivad omadused ja meetodid.
Skoobisisese muutuja saab deklareerida võtmesõnaga var.
funtion z(){ var t = 123; // muutuja t on ligipääsetav ainult funktsiooni z skoobist } z(); // Käivitame funktsiooni z, mis annab omas skoobis muutujale t väärtuse 123 try{ t; }catch(E){ alert(E.message); // t is not defined }
Meeles tuleb pidada, et märksõna var kasutamine skoobis suvalises kohas muutuja märgistamiseks, muudab muutuja terve skoobi jaoks lokaalseks. Seega üllatuste ärahoidmiseks tasub muutujad deklareerida mitte jooksvalt skoobi sees, vaid skoobi alguses.
t=1; function z(){ t++; var t = 123; } z();
Võiks arvata, et funktsiooni z() esimene rida t++; määrab muutuja väärtuseks 2 (1+1), kuna skoobisisene lähtestamine tuleb alles järgmisel real var t = 123;. Tegelikult aga saab muutuja t väärtuseks NaN (undefined + 1). Seega tuleb arvestada, et var deklaratsiooni positsioon skoobis pole oluline - muutuja on var asukohast hoolimata skoobis läbivalt lokaalne - ning selle teadmise ignoreerimine võib viia raskesti avastatavate vigade tekkimiseni.
Lokaalne skoop
Kuigi keeleliselt lokaalset skoopi JavaScriptis ei eksisteeri (olemas on vaid funktsiooni skoop), on see saavutatav läbi anonüümsete funktsioonide kasutamise.
function z(){ (function(){ var t = 2; alert(t); // 2 })() alert(t); // ReferenceError, t on defineerimata }
Näites on lokaalseks skoobiks anonüümne funktsioon mida täidetakse kohapeal. Sulud operaatorina käituvad vastavalt kontekstile - esimesed sulud tagastavad nende sees defineeritud funktsiooni ning tagastuskohale järgnevad sulud sunnivad selle funktsiooni käivitamisele. Taoline anonüümne funktsioon saab läbi sulundi kätte kõik välimise funktsiooni muutujad, kuid kohapeal defineeritud muutujatele väljast enam ligi ei pääse.
Objekti meetodite sees, säilitamaks viidet this, on mugav kasutada funktsiooni meetodit call. Sellega seome anonüümse funktsiooni objektiviite hetkel aktiivse objektiga ning ei ole vaja muretseda selle viite kadumise pärast.
var z = {a: 2}; z.meetod = function(){ (function(){ var t = 2 * this.a; alert(t); // 4 }).call(this) alert(t); // ReferenceError, t on defineerimata }
Sulundid
JavaScripti funktsioonid on kõrgemat-järku objektid, mis tähendab, et funktsiooni skoobis saab defineerida siseseid täiendavaid funktsioone. Neid siseseid funktsioone saab omakorda käivitada, omistada muutujatele väärtuseks või tagastada võtmesõnaga return välise funktsiooni tagastusväärtusena. Defineeritud sisemised funktsioonid omavad ligipääsu lisaks enda skoobis olevatele väärtustele ja objektidele ka funktsiooni loonud välise funktsiooni skoobi väärtustele ja objektidele. Sellisel viisil ühendatud skoobid moodustavad sulundi.
Sulundi näide:
function generaator(){ var i = 0; return function(){ return i++; } } genereeri = generaator(); genereeri(); // 0 genereeri(); // 1 genereeri(); // 2
Funktsiooni generaator() skoobis asub lokaalne muutuja i, mille algväärtus on 0. Tagastatav sisemine anonüümne funktsioon kasutab tekkinud sulundi võimalusena välise funktsiooni muutujat enda andmete salvestamiseks. Seega iga funktsiooni genereeri käivitamisega muudetakse välise funktsiooni muutuja i väärtust ning tagastatakse see väljakutsekohta.
Funktsiooni saab deklareerida teise funktsiooni sees ainult funktsiooni peamises blokis, mitte sisestes (näites for lause blokis vms.). Funktsiooni literaale aga saab seada igal pool.
function master(){ // alamfunktsiooni deklareerimine function slave(){ // funktsiooni sisu } for(var i=0; i<10; i++){ // funktsiooni literaal var literaal = function(){ return i*i; } alert(literaal()); } } master();
Rekursioonid
JavaScript võimaldab funktsioonide rekursioone - funktsioon saab kutsuda välja iseennast.
Rekursiooni näide:
function z (x, i){ var i = i || 0; if(i<10){ alert(x); z(x, ++i); } } z("tere maailm!");
Kui i väärtus funktsioonis z() on väiksem kui 10, siis funktsioon kutsub end ise uuesti välja koos 1 võrra suurema i väärtusega. Funktsiooni z(x) käivitamise tulemuseks kuvatakse ekraanil 10 korda muutuja x väärtus, milleks näites on „tere maailm!“.
Funktsioon saab kutsuda iseennast kas oma nime abil või parameetri arguments meetodiga callee.
function a(){ a(); } function b(){ arguments.callee(); }
Lihtsamad rekursioonid saab asendada tsüklitega ja see on ka soovitatav praktika, kuna rekursioonid võivad intensiivsemal kasutamisel osutuda vägagi ressursivaenulikeks. Keerulisematel juhtudel kus on näiteks vaja toimetada teadmata suurusega puukujuliste objektidega (DOM), võib tsüklite kasutamine olla üsna keeruline, rekursiooniga aga saab suhteliselt lihtsalt selliseid probleeme lahendada.
Järgmises näites kasutame rekursioone numbrite liitmisel. Koostame funktsiooni, mis võtab suvalise arvu parameetreid ja liidab nende väärtused kokku. Massiivist liidetava puhul summeeritakse eelnevalt massiivi elemendid rekursiooni abil, kutsudes hetke funktsiooni välja nii, et kutsutava funktsiooni parameetriteks oleksid massiivi elementide väärtused.
function sum(){ var summa = 0; for(var i=0, len = arguments.length; i<len;i++){ // juhul kui parameeter on number, liidame selle koheselet if(typeof arguments[i] == 'number'){ summa += arguments[i]; } // massiivi puhul kutsume funktsiooni uuesti välja if(arguments[i].constructor == Array){ // meetod apply esitab massivi elemendid eraldi parameetritena // func.apply(this,[1,2,3]) == func(1,2,3); // lisaks määratakse ka nõutud viit this, mis hetkel küll ei ole aktuaalne summa += arguments.callee.apply(this, arguments[i]); } } return summa; } alert(sum(1,2,[3,4,[5,6]])); // 21 (1+2+3+4+5+6)
Järgmise näitena vaatame ükshaaval läbi kõik puukujulise objekti (näiteks dokumendiobjektimudel DOM, või XML objekt) elemendid
function jalutaPuu(haru) { if (haru == null) // return; // tee midagi puu haruga for (var i = 0; i < haru.childNodes.length; i++) { jalutaPuu(haru.childNodes[i]); } } jalutaPuu(document.body);
Funktsioonide atribuudid
Funktsioonidel on olemas ka mõningad „sisse-ehitatud“ atribuudid ja meetodid, mis päritakse Function.prototype prototüübilt.
length
Funktsiooni atribuut length väljastab funktsiooni deklaratsioonis määratud sisendparameetrite arvu. Näiteks saab sellega kontrollida, kas funktsiooni käivitamisel on sisestatud kõik vajalikud parameetrid või mitte.
function a(b,c,d){ if(arguments.callee.length != arguments.length){ throw new Error("Parameetrite arv ei ole korrektne!"); } return b+c+d; } a(1,2); // veateade a(1,2,3); // 6
prototype
Atribuut prototype viitab eeldefineeritud nn. prototüübiobjektile, mis hakkab rolli mängima juhul, kui funktsiooni kasutatakse konstruktorina koos operaatoriga new. Sellisel juhul pärib konstruktoriga loodav objekt funktsiooni prototüübiobjekti, saades seda kasutada viite this abil. Lähemalt saab prototüüpide kohta lugeda Objektide peatükist.
call
Funktsiooni meetod call käivitab funktsiooni koos meetodiga etteantud parameetritega ning objektiviidaga this. Meetodi skoop muudetakse objektiviidaga orginaalsest asukohast etteantud objekti juurde.
var part = { hääl: "prääks", prääksu: function(kordadeArv){ for(var i=0; i<kordadeArv; i++){ alert(this.hääl); } } } var lehm = { hääl: "ammuuu" }; part.prääksu(3); // kolm korda teade "prääks" part.prääksu.call(lehm, 3); // kolm korda teade "ammuu", objektiviidaks on objekt lehm
Objekti part meetod prääksu väljastab kolm korda objekti lehm väärtuse hääl, kuigi objektil lehm endal vastav funktsioon üldse puudub.
apply
apply on praktiliselt sama meetod, mis call, ainult et parameetrite ükshaaval esitamise asemel võetakse sisendiks ühtne massiiv, mille elemendid edastatakse vastavalt kutsutava meetodi parameetritena.
function a(b,c,d){ // funktsiooni sisu } var massiiv = [1,2,3]; a.apply(null, massiiv);
Käidud rada: • objektid • advanced • stringid • massiivid • script_tag • nimeruumid • operaatorid • matemaatika • sissejuhatus • funktsioonid