"javascript"
Wednesday, June 15, 2011

Array.forEach imedemaa

Sattusin Twitteris nägema JavaScript: The Definitive Guide autori David flanagani postitust

And if your browser doesn’t have Array.forEach, use this mind-bender: Array.forEach = Function.prototype.call.bind(Array.prototype.forEach) link

Ilmnes et tegu on tõesti paraja mind-bender’iga, võttis tükk aega, enne kui suutsin selle rea dešifreerida. Milles siis asi?

Array.forEach = Function.prototype.call.bind(Array.prototype.forEach);

Antud rida lisab Array objekti uue meetodi forEach, mis käitub järgnevalt

Array.forEach(array, callback)

array on massiiv või massiivilaadne objekt (näiteks NodeList) ning callback on iteraator, mis saab ükshaaval parameetrina massivi elemendid.

Kuidas aga suudab rida Function.prototype.call.bind(Array.prototype.forEach) sellise asja tekitada?

Tuleb välja, et väga lihtsalt.

Esimene oluline osaline lauses on meetodi bind rakendamine. Antud meetod seob parameetrina saadud väärtuse endast vasakul oleva objekti (milleks hetkel on funktsioon call) kontektsimuutujaga this. Oluline on, et seda konteksti ei ole võimalik enam hiljem üle kirjutada!

Seega nüüd on meetodi call kontekstiks enam mitte Function.prototype vaid Array.prototype.forEach. Lihtsustatult võib seega sama lause kirjutada nii:

Array.forEach = Array.prototype.forEach.call

Tavaliselt peaks sellise tehte tulemusena kontekst jälle muutuma. Objekti Array uus meetod peaks selle lause järgi olema sama mis call kuid nüüd oleks kontekstiks juba Array

Array.forEach = Array.prototype.forEach.call ->  Array.__call__

Kuid see siiski pole nii, sest esimene bind juba sidus konteksti ära (selleks sai Array.prototype.forEach) ning see enam edasistes tehetes ei muutu.

Kokkuvõttes ongi tulemus selline, et järgmine lause

Array.forEach(array, callback)

On tegelikult täpselt sama, mis kirjutada

Array.prototype.forEach.call(array, callback)

Kuid ilmselgelt lühemas ja käepärasemas vormis.

Saturday, May 28, 2011

JavaScript edasijõudnutele - eestikeelne konspekt

Panin üles oma konspekti “JavaScript edasijõudnutele,” mida viimasel ajal kasutanud olen - näiteks JavaScripti koolitamisel :)

Konspekti leiab aadressilt tahvel.info/javascript:advanced

Sama tekst on võimalik alla laadida ka e-lugeri formaadis:

Tekst on jagatud Creative Commons Autorile viitamine + Jagamine samadel tingimustel 3.0 Eesti (CC BY-SA 3.0) litsentsiga

Wednesday, April 27, 2011

Objekti meetodi kontekst JavaScriptis

Kuidas saada teada, millisele objektile viitab this vaikimisi kui tegu on objekti meetodiga? call, apply ja bind abil saab seda ka ise muuta, kuid mis on selleks vaikimisi?

Rusikareegel on, et tuleb vaadata funktsiooni käivitamise hetkel (st. sellel kohal, kus funktsiooniväärtuse taga on sulud) käivitatava väärtuse vasakut poolt - misiganes sealt vastu ei vaataks, see ongi kontekst.

Math.round();  // meetodi *round* kontekst on *Math*
window.Math.round(); // kontekst on *window.Math*

Juhul kui seal pole midagi, on kontekstiks window.

alert(); // funktsiooni *alert* kontekst on *window*

Kui tegu on callback funktsiooniga, siis on kõik sama - arvestada ei tule mitte selle seadmise hetke, vaid käivitamise hetke

function foo(callback){
    callback();  // <-- siin toimub käivitamine
}
foo(console.log); // <-- siin seadmine

Näites ei ole käivitatava meetodi log kontekstiks mitte console vaid window! Kuna konteksti seab käivitamise hetk ning lause callback(); juures, erinevalt lausest foo(console.log) enam konteksti määratud pole - vasakul pool kävitatavat väärtust pole märgitud midagi.

Et mitte jätta konteksti juhuse hooleks tasub tagasikutsefunktsioonide juures kasutada väärtuse seadmise hetkel meetodid bind (ES5 lisa, töötab vaid moodsaimates brauserites, samuti ka NodeJS platvormil) või käivitamise hetkel meetodeid call või apply.

// tagasikutsefunktsiooni kontekstiks saab *console*
foo(console.log.bind(console));

või

// käivitatava funktsiooniväärtuse kontekst on *console*
function foo(callback){
    callback.call(console);
}

Täpsustuseks märgiks veel ära, et kirjeldatud kontekst this olulisus seisneb selles, et kontekst viitab “iseendale”. See tähendab, et objekti meetodi sees saab kasutada objekti muid omadusi ja meetodeid, ilma et oleks vaja teada objekti nimetust vms viidet.

foo = {
    bar: 1,
    foobar: function(){
        alert(this.bar); // <-- kontekstiks on objekt *foo*
        // *this.bar* on sama mis *foo.bar*, kuid konteksti
        // tõttu pole vaja *foo* nime teada
    }
} 

NB! Kui tagasikutse funktsioonile on meetodiga bind kord juba kontekst seatud, siis seda call või apply abile muuta enam ei saa.

function bar(){
    alert(this.baz);
}
function foo(callback){
    callback.call({baz:1}); // <-- ei muuda enam midagi
}
foo(bar.bind({baz:2}); // <-- bind seab konteksti
Wednesday, April 13, 2011

iPhone rakenduse tegemine

Eelmises postituses mainisin, et minu matemaatiliste valemite rakendust on praeguseks alla laetud 1000 korda, siinkohal siis valgustaks veidi tagamaid.

Ma ei teinud seda rakendust erilise eesmärgiga, soov oli vaid proovida läbi kogu töövoog alates sellest et on idee, kuni selleni, et rakendust saab AppStorest alla laadida. Idee teket täpselt ei mäleta, igatahes sai selleks matemaatika valemite kuvamine - sisuliselt on tegu spikriga, valid teema ning näed vastavaid valemeid (nt. ruutvõrrandi valemit). Kuna eeldatav sihtrühm krediitkaarti ei oma (tegu siiski kooliõpilastega), siis oli plaaniks teha rakendus tasuta ja selle eest mitte raha küsida.

C keeles ma suurt ei orienteeru, viimased katsetused jäid aastate taha kui tegin TTÜ’s mingeid kodutöid, seega sellele väga keskenduda ei tahtnud. Õnneks on praeguseks saada ka erinevaid toolkit‘e, mis võimaldavad mugavamalt hakkama saada. Minu esimeseks valikuks sai PhoneGap, mis on sisuliselt UIWebView wrapper. Teed HTML lehe, paned selle JavaScriptiga “elama” ja pakid rakenduseks kokku.

Twitterist sain vihje, et on olemas ka parem süsteem nimega Appcelerator, mis sisuliselt on sama, aga ainult UIWebView asemel pakub ehitusklotsidena kõiki native UI elemente ning selmet veebilehena asja kuvada, kompileerib eraldiseisvas failis oleva JavaScripti koodi iPhonele arusaadavaks - ühesõnaga veebilehe asemel kompileeritakse programm native app‘iks.

Struktuur

Rakenduse struktuur on väga lihtne. Põhivaade koosneb kahe elemendiga TabGroup objektist. Esimene element sisaldab “sisukorda”, teine valitud teema valemeid.

Sisukorravaade koosneb TableView elemendist, mille sees on üksikud TableViewRow elemendid. Kusjuures, igale reale on seatud eraldi “onclick” kuular, nii et kui mõnel real vajutada, avatakse valemite vaates vastavad valemid.

Valemite vaates on põhielemendiks WebView, mis kuvab lokaalseid HTML faile. Kõik valemid on GIF pildid, mis asuvad HTML failidega samas kaustas rakenduse sees. Uue teema valimisel näidatakse kasutajale aktiivsuse indikaatorit, mille kaotab WebView küljes olev onload sündmus.

Administreerimine

Valemite sisestamiseks kirjutasin lihtsa veebiliidese, mis võimaldab tekitada kategooriad ning nende alla valemeid sisestada. Kui valemid on sisestatud, käivitan täiendava skripti, mis genereerib rakenduse jaoks vajalikud HTML failid ning koostab ka JSON formaadis sisukorrafaili, mille alusel rakendus faile näidata oskab.

Sisukorrafaili sisu on järgmine:

[
    {
        "title": "Aritmeetika",
        "file": "f3f80b155d7d9af6c53bcfe23f1ea1a5.html"
    },
    {
        "title": "Algebra",
        "file": "589e1e9e690257c13858583381062e49.html"
    },
    {
        "title": "Võrrandite lahendid",
        "file": "54a2708351b891702815e787feed5f69.html"
    },
    {
        "title": "Planimeetria",
        "file": "7a1970deec26f4b79b4c245c03c5f345.html"
    },
    ......
]

Nii on rakenduse sisu lihtne uuendada - tuleb genereerida uued failid (iga kord on failinimed unikaalsed, et vältida puhverdamist) ja rakendus loeb need andmed kindla nimega JSON failist kenasti sisse.

Kood

Kood on kirjutatud täielikult JavaScriptis (lisaks standardsetele võimalustele on kasutusel Appceleratori API’id). Toon siin ära põhiloogika bloki, mis laeb JSON failist sisukorra ja genereerib selle alusel sisukorra tabeli read.

// funktsioon laeb sisukorrafaili ja genereerib
// massiivi tabeli rea elementidega
function generateIndex(){
    var data = [], json, rows = [], row,
        file = Titanium.Filesystem.getFile(
            Titanium.Filesystem.resourcesDirectory,
            "topics", "quefile.json");

    // lae failist JSON
    if (file.exists()) {
        json = file.read();
        try{
            data = JSON.parse(json);
        }catch(E){}
    }

    // koosta tabeli read
    for(var i=0; i<data.length; i++){
        if(!i){
            // globaalne muutuja
            firstpage = data[i].file;
        }
        row = Titanium.UI.createTableViewRow({
            title: data[i].title
        });

        // kuna tegu on tsükliga, loo lokaalne kontekst
        // kahjuks bind puudub, seega vaja nikerdada
        (function(f, t){
            row.addEventListener("click", 
                function(){displayPage(f, t)},
                false);    
        })(data[i].file, data[i].title);

        rows.push(row);

    }
    return rows;
}

Kusjuures rakenduse skriptiosa on ainult 99 rida pikk ja saaks ka veel lühemalt, kuna see on optimeerimata, sisaldab katsete käigus tekkinud jäänukelemente jne.

Valmis rakenduse saab iPhone, iPod ja iPad seadmesse laadida siit.

NB! Et saada õigus iseenda(!!!) telefonis rakendust katsetada ning hiljem valmis rakendus AppStore‘i üles laadida, tuli liituda Apple Developers programmiga, mille aastatasu on $99. Kusjuures, makset ei saa teha online vaid tuleb välja printida avalduse vorm, täita see ära (sh. kanda vormile krediitkaardi andmed) ja see faksida (!!!) Apple numbrile. Kasutasin faksimiseks online teenust, kuhu laadisin üles PDF faili (allkirja ei pannud ise, vaid kopeerisin eesti.ee portaalist enda rahvastikuregistri andmete juurest). Kahjuks ei kasutanud SEB virtuaalkaarti vaid reaalset kaarti ning et igaks juhuks vältida tulevasi probleeme panin kasutatud krediitkaardi hiljem kinni. SEB virtuaalkaardiga seda muret poleks tekkinud.

Thursday, November 11, 2010

JavaScripti omistamislause

Ja nüüd midagi, mis on mul huvitaval kombel siiani kahe silma vahele jäänud, aga mida lugesin Stoyan Stefanovi raamatust JavaScript Patterns ja mis tagantjärele tundub täiesti loogiline.

var a = b = c = 5;

Ei ole sugugi sama, mis

var a = 5;
var b = 5;
var c = 5;

Vaid hoopis

var a = 5;
b = 5;
c = 5;

Ehk et muutujatest b ja c saavad lokaalsete asemel hoopis globaalsed muutujad!

Samal teemal veel

Igaks juhuks märgin ära veel samasse teemasse kuuluva, aga juba tuntud probleemi

a = b = [];
c = d = {};
e = f = 5;

Sellise omistamise korral ei saa me mitte 2 massiivi-, kaks objekti- ja kaks numbriväärtust, vaid kaks numbriväärtust (e ja f, mõlema väärtuseks nr 5) ja ühe massiivi- ning ühe objektiväärtuse.

a ja b ei ole mitte eraldi väärtused, vaid kaks muutujat, mis viitavad ühele ja samale massiiviväärtusele. JavaScriptis on objektide edastamine (ka massiiv on objekt) alati BY REFRENCE.

Seega lause

a = b = [];

võib lahti kirjutada ka kui

a = (b=[]);

ehk

b = [];
a = b; // BY REFRENCE!
Thursday, October 28, 2010

JavaScripti testimine

Kuigi testimise valdkond on suur ja lai, siis üks kindel asi mida tagada tuleb, on eri platvormide testimine. Alati ei ole aga kõiki võimalikke brausereid ja operatsioonisüsteeme käepärast ja tuleb kasutada alternatiivseid vahendeid. Kõige kiirem ja lihtsam nipp erinevate platvormide testimiseks oleks browsershots lehe kasutamine:

  • tee testleht, mis kontrollib erinevaid kindlaid asju (unit tests)
  • tulemused peaksid olema esitatud tabelina (et oleks kergelt aru saada, mis mida tähendab) ja testi läbinud read oleksid näiteks rohelised ja mittetöötavad punased
  • mine browsershots.org lehele ja sisesta testlehe aadress
  • oota kuni pildid on valmis ning vaata nendelt, millised testid on läbi kukkunud

Alati ei anna browsershots pildid õiget tulemust, kuna brauserid ei ole kontrollitud. Võibolla näiteks on konkreetne brauser seadistatud nii, et test ei saagi seal läbi minna (näiteks on küpsised välja keeratud, kui neid oleks vaja jne), samas aga kuna puudub info konfiguratsiooni kohta, on raske ka järeldusi teha. Küll aga kui kõik on roheline, võib omadega rahul olla.

Mina olen seda strateegiat kasutanud näiteks jStorage testimiseks (testleht).

Thursday, October 21, 2010

Veebipõhised protokollide haldajad

Kes on vähegi HTML’iga kokku puutunud, teab et linkidel käib ees protokolli nimetus- tavalingi puhul on selleks http: või https:, FTP serveri puhul ftp:, e-maili aadresside korral mailto: jne. Klikkides lingil, avatakse protokolliga seotud rakendus. Veebilinkide puhul on selleks brauser ise, aga ftp, mailto jne juba reeglina mõni muu.

Erinevad aplikatsioonid saavad registreerida brauseris ka oma kohandatud protokolle, nii teeb näiteks Skype - skype:kasutajanimi?call. Vähe aga teatakse, et näiteks Firefox brauseris saab protokolli kindla rakendusega siduda ka suvaline veebiteenus - sellisel juhul on protokolli avavaks rakenduseks enam mitte väline rakendus vaid määratud veebilink, mis saab parameetriks lingi väärtuse. Nii näiteks saaks veebipõhine e-posti klient suunata endale kõik mailto: lingid - kasutaja klikib sellisel lingil, kuid Outlooki või Thunderbirdi avamise asemel avatakse veebipõhine e-posti teenuse leht.

Protokolli saab enda veebiteenusega siduda järgmise käsuga:

navigator.registerProtocolHandler(protocol,
          handling_url,
          name);

Kus protocol on protokolli eesliide (näiteks mailto), handling_url on aadress, kuhu päring suunatakse (aadressis olev %s asendatakse lingi väärtusega, nb - aadress peab asuma samas server, kui päringut teostav veebileht) ja name inimloetav protokolli nimetus. Loomulikult ei lisata sellist protokolli registreerimist automaatselt, vaid enne küsib brauser kasutaja luba nagu kõigi muude taoliste tegevuste korral.

Näide

Kui kasutajalt küsida mailto linkide suunamist järgmise käsuga

navigator.registerProtocolHandler("mailto",
          "http://emaili-server.ee/new?to=%s",
          "Minu E-post");

ja seejärel kasutaja klikib suvalisel veebilehel järgmise kujuga lingil

<a href="mailto:kasutaja@server.ee">saada kiri</a>

siis avab brauser veebilehe aadressiga

http://emaili-server.ee/new?to=mailto:kasutaja@server.ee

Katsetada saab siin ja täpsemalt uurida Mozilla MDC Web-based protocol handlers lehelt.

Töötab - Firefox 3.x. Webkiti core’s on olemas juba aasta aega kuid miskipärast ei Chrome ega Safari seda veel ei toeta. Tulevikus aga kindlasti. IE kohta ei oska öelda.

Monday, October 11, 2010

Mailiserveri tagasilöök

Tundub, et sain esimese tõsise tagasilöögi oma emailiserveri projekti juures - MongoDB GridFS moodul on pisut poolik ja ei tööta veel päris nii nagu vaja. Minu konkreetne probleem seisneb selles, et ma ei suuda lugeda korralikult faile, mis sisaldavad suuremaid baite kui 127 (ehk siis need baidid, kus 8nda positsiooni väärtus on seatud). Millegipärast on kõik selliste baitide väärtuseks 253 (FD).

Kokkuvõttes - ma ei saa kirja manuseid kettale salvestada. Tava-failisüsteem on suur ei-ei, aga GridFS asemele ei ole ka nagu midagi kuskilt võtta.

Primitiiv ja objektitüübid JavaScriptis

JavaScriptis saab numbreid, teksti jms esitada nii primitiivväärtusena kui ka objektina. Tehete puhul ei ole kummalgi variandil erilist vahet - numbrite liitmisel on tulemus ikka sama, hoolimata sellest kas kumbki operand on primitiivnumber (n. 10) või objekt (n. new Number(10)), vastus on ikka täpselt sama.

var nr_primitiiv = 10,
    nr_objekt = new Number(10);
nr_primitiiv + nr_objekt; // 20

Samuti saab primitiivtüübina defineeritud väärtust kasutada sama tüübi objekti väärtusena, sellisel juhul teisendatakse väärtus operatsiooni läbiviimise ajaks objektiks.

var nr_primitiiv = 10;
    nr_objekt = new Number(10);
nr_objekt.toFixed(2); // "10.00"
nr_primitiiv.toFixed(2); // "10.00"

Kusjuures viimast saab teha ka otse numbri endaga - arvestada tuleb ainult, et numbri ja punkti vahele (objekti meetodi eraldaja) tuleb jätta tühik, kuna vastasel juhul peab interpretaator punkti mitte meetodi, vaid komakoha eraldajaks.

10 .toFixed(2); // "10.00"

Samuti saab kasutada nurksulge, sellisel juhul pole vaja isegi tühikut kasutada

10["toFixed"](2); // "10.00"

Erinevused

Seega, mis kasu on üldse primitiivväärtuse objektina defineerimisel, kui toimingute läbiviimisel pole mingit vahet?

Tuleb välja, et üks erinevus siiski on. Selle erinevuse tuvastamiseks kontrollime kõigepealt väärtuste tüüpe.

typeof 10; // "number"
typeof new Number(10); // "object"

Nagu näha, on tavanumbri tüübiks number, aga objektina defineeritud väärtuse tüübiks oodatult object. Mis aga on nende kahe tüübi vahe? Vahe on väärtuse edastamises - kui primitiivväärtuse edastamisel edastatakse väärtus (by value), siis objekti edastamisel edastatakse objekt ise (by ref).

Kuna aga numbriobjekt on objekt, siis saab sellele lisada omadusi ja meetodeid nagu kõikidele teistele objektidele ning sellise numbriobjekti edastamisel (näiteks funktsiooni parameetrina) liiguvad need lisatud meetodid ja omadused numbriobjektiga kaasa.

var nr_objekt = new Number(10);

// defineeri numbriobjektile täiendav meetod
nr_objekt.teavita = function(){
    alert(this);
}

// funktsioon ootab sisendiks objekti meetodiga "teavita"
function teavitus(nr){
    nr.teavita();
}

// edasta numbriobjekt funktsioonile - by ref
teavitus(nr_objekt); // alert(10);

Ainukseks probleemiks on vaid see, et taolise objekti numbriväärtust ei saa enam muuta. Muutes objekti primitiivväärtust, lõhutakse objekt selle väärtuse ümber. Küll aga saab muuta väärtust, mis maailmale välja paistab, vt. järgmist punkti.

Veel rohkem lahedust

Numbriprimitiivi ja numbriobjekti liitmisel õnnestub tehe seetõttu, et objekti väärtuse puhul ei kasutata mitte objekti ennast, vaid selle meetodit valueOf, mille väärtuseks on samuti numbriprimitiiv.

10 + new Number(10).valueOf()

JavaScriptis aga on enamvähem kõik ülekirjutatav. Seega võib proovida üle kirjutada ka valueOf meetodit.

var a = new Number(10);
a.valueOf = function(){
    return 5;
}
5*a; // 25 (5 * 5)

NB!

Kasutasin siin näitena vaid numbriprimitiive ja -objekte, aga sama laieneb ka kõikidele teistele väärtustele (string, boolean). Erineb vaid objekti väärtuse teisendamise meetod, stringi puhul on selleks valueOf asemel toString. Täpsemalt saab vaadata kuidas väärtusi konteksti alusel teisendatakse siit.

Saturday, October 9, 2010

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).

My Tumblr Likes ♥