Monday, October 11, 2010

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.