Objektid JavaScriptis
Panen siia kirja väikese ülevaate objektidest JavaScriptis.
Objektid JavaScriptis on lihtsad võtme-väärtuste paaride kogumid, millega on täiendavalt seotud veel ka kontekstimuutuja this. Juhul kui võtme väärtuseks on funktsioon, nimetatakse seda võtit objekti meetodiks, muudel juhtudel aga omaduseks. Objektideks võib pidada lisaks spetsiaalselt defineerituile JavaScriptis ka kõiki teisi kasutatavaid väärtusi, sealhulgas numbreid, teksti, loogilisi väärtusi, funktsioone jne. Kõikidel nendel väärtustel on olemas omad omadused ja meetodid. Näiteks numbriprimitiiv 10 ei ole iseeneest küll objekt, vaid 64 bitine number, aga proovides kasutada seda numbrit objekti kontekstis (n. 10 .toFixed(2)), teisendatakse primitiivtüüpi number taustal tehte läbiviimise ajaks automaatselt Number tüüpi objektiks.
Kui teistes keeltes defineeritakse reeglina kõigepealt looodavat objekti kirjeldav klass ja seejärel tuletatakse loodud klassi alusel objektid (kusjuures samast klassist tuletatud objektid on struktuurilt identsed), siis JavaScriptis klassi mõiste puudub ja objektide loomine on tunduvalt vabam. Alustada võib näiteks tühjast objektist (selleks saab kasutada objektiliteraale, loogelisi sulge var tyhi_objekt = {};), millele saab siis hiljem vajadusele vastavalt uusi omadusi/meetodeid lisada ning vanu kustutada.
Klasside asemel on JavaScriptis kasutusel nn. prototüüpobjektid. Ühe objekti prototüübiks võib olla suvaline teine objekt. Kui erinevad objektid on üksteise prototüüpideks, tekib sellega prototüübiahel - iga ahelas olev objekt saab kasutada kõiki ülemistes kihtides defineeritud prototüüpide omadusi ja meetodeid kui endagi omi. Juhul kui muuta prototüüpobjektis mõnd meetodit või omadust, muutub see automaatselt ka kõikides sellest prototüübist põlvnevates objektides. Samas ülespoole muutused ei kajastu - muudatused objektis selle prototüüpi ei mõjuta. Juhul kui objektis kirjutada mõni prototüübilt päritud omadus või meetod üle, katkeb selles kohas ahel ning objekt saab endale “isikliku” väärtuse (seda kas tegu on “isikliku” või päritud väärtusega, saab kotrollida meetodiga hasOwnProperty).
Ühetüübiliste objektide loomiseks saab klasside asemel kasutada konstruktorfunktsioone, kuid konstruktor pole sisuliselt midagi muud kui otsetee prototüüpobjekti kloonimisest ja initsialiseerimisfunktsioonist. Kuna JavaScripti esialgne disain jäi suhteliselt poolikuks, siis kuni praeguseni on konstruktorid olnud ainsad reaalsed (brauseriülesed) vahendid prototüübiahelate loomiseks. Alles ECMAScript5 tutvustas käsklust Object.create, mis võtab parameetriks loodava objektile omistatava prototüübi. Osad brauserid on mõnda aega toetanud ka objektide omadust __proto__, mis on otselingiks prototüüpobjekti juurde. Tegu on siiski mittestandardse ja vähe toetatud omadusega, seega reaalselt seda kasutusele võetud kunagi ei ole.
Objektide kirjeldamine
Objekte kirjeldatakse loogeliste sulgude vahel olevate komadega eraldatud väljadega, kus väljad koosnevad võtme nimest ja väärtusest. Võtme nime (võib olla nii jutumärkide vahel, kui ka ilma) järel on võtit ja selle väärtust eraldav koolon.
{
omadus: "väärtus1",
"meetod": function(){}
}
NB! Tähele tasub panna, et võti ”omadus” on esitatud ilma-, aga ”meetod” koos jutumärkidega. Jutumärkide kasutamine on oluline juhul kui võtme nimes on sümboleid, mida ei saa muutuja nimes kasutada, näiteks kui soovitakse kasutada miinusmärki või tühikut.
{
"eba-standardne nimetus":"väärtus"
}
Objekti omadustele ja meetoditele saab ligi kahel viisil - a) punktnotatsiooniga, kus objekt ja selle võtme nimetus on eraldatud punktiga või kui võtme nimetus on nurgelistes sulgudes objekti taga.
var objekt = {"nimi":"objekt"};
objekt.nimi === objekt["nimi"];
Kontekstimuutuja “this”
Iga objektiga on seotud ka kontekstimuutuja this mis võimaldab objekti meetodites pääseda ligi sama objekti teistele omadustele ja meetoditele. this sõltub ka skoobist. Kui tegu ei ole otseselt objekti meetodiga (näiteks anonüümne funktsioon meetodi sees), siis viitab this globaalsele objektile, milleks brauseri puhul on alati window.
var objekt = {
meetod: function(){
this == objekt; // true, meetodi skoop
function sisemine(){
this == window; //true, anonüümne skoop
}
}
}
Konteksti on võimalik ka funktsioonile ise ette anda, kasutades selleks funktsiooni meetodeid call ja apply. Sisuliselt on tegu samade meetoditega, ainult et kui call edastab argumendid nii nagu ta need ise saab, siis apply kasutab funktsioonile edastatavateks argumentideks sisendina saadud massiivi väärtusi.
function kontekst(){
alert(this==window);
}
kontekst(); // true, vaikimisi kontekstis
kontekst.call({}); // false, kontekstiks on anonüümne objekt
Alati ei ole aga vaja funktsiooni kohe käima panna, vaid millalgi hiljem (sündmuste jms tagasikutsed). Selliseks juhuks võimaldab ECMAScript5 kasutada meetodit bind, mis seob funktsiooni kontekstiga. Kuna paljud brauserid veel ECMAScript5 ei toeta, tuleb nendes see meetod ise defineerida.
var context = {text:"text to be displayed"};
function callback(){
alert(this.text);
}
window.onload = callback.bind(context);
NB! meetod bind mitte ei käivita funktsiooni ja ei tagasta selle tagastusväärtust nagu näiteks call ja apply, vaid tagastavad sama funktsiooniobjekti, kuid koos seatud kontekstiga! Väga lihtsustatult näeb sidumine välja nii:
Function.prototype.bind = function(context){
var func = this;
return function(){
func.call(context);
}
}
Funktsiooni meetodi näites saab func väärtuseks käesoleva funktsiooni enda. Seejärel tagastatakse väljakutsekohta anonüümne funktsioon. Kui nüüd see funktsioon käima panna (eelnevas näites window.onload sündmuse korral) käivitataksegi juba rida func.call(context) ehk et käivitatakse originaalne funktsioon, millele määratakse teine kontekst (sulundi kaudu tulnud context).