Am văzut în episodul XVII cum putem grupa diferite informații în obiecte. Am lucrat cu obiecte simple, dar este ușor să ne imaginăm că, pe măsură ce obiectele devin mai complexe, e destul de complicat să stabilim valori pentru fiecare proprietate în parte.
Constructori
Avem la dispoziție posibilitatea de a scrie funcții care creează obiectele noastre. De exemplu, pentru o persoană ale cărei proprietăți sunt numele și vârsta, am putea avea o funcție cu doi parametri (un string și un număr întreg) care să creeze obiectul respectiv.
Putem scrie o funcție simplă care să returneze un obiect:
1 2 3 4 5 6 |
function creeazaPersoana(unNume, oVarsta) { return { nume: unNume, varsta: oVarsta } } |
Este o variantă bună, chiar foarte utilă în unele situații. Funcția ar putea fi folosită astfel:
1 |
var elev = creeazaPersoana("Dragos", 17) |
Există și o alternativă: un constructor. Constructorii sunt funcții speciale care creează obiecte. Spre deosebire de funcțiile obișnuite, constructorii returnează automat obiectul creat.
Pot exista mai mulți constructori, cu parametri diferiți, atâta timp cât nu există pericolul să apară confuzii (vom vedea puțin mai încolo).
Să transformăm funcția noastră într-un constructor:
1 2 3 4 |
function persoana(unNume, oVarsta) { this.nume = unNume this.varsta = oVarsta } |
Observăm că nu apare o instrucțiune return; se va returna automat, la sfârșit, obiectul creat. Mai mult despre acel this vom vedea în episodul XX.
Pentru a utiliza constructorul, va trebui să îl apelăm în momentul creării obiectului. Crearea se va realiza acum folosind cuvântul new, urmat de apelul constructorului. Un exemplu ar putea fi:
1 |
var elev = new persoana("Dragos", 17) |
Putem avea și constructori fără parametri; un astfel de constructor poartă denumirea de constructor implicit. Dacă dorim, putem să definim:
1 2 3 4 |
function persoana() { this.nume = "Dragos" this.varsta = 17 } |
Acum, toate obiectele create folosind apeluri de genul new persoana() vor crea obiecte pentru care numele este Dragoș și vârsta este 17.
Avem deja doi constructori; putem să mai adăugăm unul. De exemplu, poate dorim să creăm multe persoane care au 18 ani și am vrea să specificăm doar numele. Am putea avea constructorul:
1 2 3 4 |
function persoana(unNume) { this.nume = unNume this.varsta = 18 } |
Acesta ar putea fi folosit astfel:
1 |
var p = new persoana("Antoniu") |
Dar, să presupunem că dorim să creăm acum multe persoane care au 16 ani. Am putea crede că putem crea un nou constructor:
1 2 3 4 |
function persoana(unNume) { this.nume = unNume this.varsta = 16 } |
De fapt, nu va funcționa așa cum ne-am dori fiindcă acești ultimi doi constructori nu pot fi diferențiați în momentul apelului. Dacă ar exista amândoi, atunci un apel ca cel de mai sus (care creează o persoană cu numele Antoniu) nu ar ști exact pe care dintre ei să îl apeleze. Nu ajută cu nimic dacă schimbăm numele parametrului; tot nu există posibilitatea de a diferenția constructorii. Va fi apelat doar unul dintre constructori.
Avem deja trei constructori și îi putem folosi pe toți:
1 2 3 |
var p1 = new persoana() var p2 = new persoana("Antoniu") var p3 = new persoana("Sofia", 15) |
Am creat o persoană cu numele Dragoș și vârsta de 17 ani, una cu numele Antoniu și vârsta de 16 ani și una cu numele Sofia și vârsta de 15 ani.
Obiecte în obiecte
Proprietățile obiectelor pot fi, la rândul lor, obiecte; să ne imaginăm că persoanele noastre au și o adresă. O adresă ar fi un obiect care să conțină informații cum ar fi strada, numărul, orașul etc. Pentru simplitate, ne vom limita la stradă și număr. Am putea construi o adresă astfel:
1 2 3 4 |
function adresa(oStrada, unNumar) { this.strada = oStrada this.numar = unNumar } |
Acum, putem modifica constructorii care creează persoane pentru a avea și o adresă; pentru simplitate, vom avea doar un constructor care să ne permită să precizăm numele, vârsta și adresa.
1 2 3 4 5 |
function persoana (unNume, oVarsta, oAdresa) { this.nume = unNume this.varsta = oVarsta this.adresa = oAdresa } |
Să vedem cum accesăm obiectele din obiecte. Nu e foarte complicat; trebuie doar să adaugăm puncte...
Mai exact, dacă elev este obiectul, atunci elev.adresa este obiectul din interior (o proprietate o primului obiect) și elev.adresa.strada, respectiv elev.adresa.numar, sunt proprietățile acestui obiect din interior.
Nu este nepărat nevoie să ne oprim aici. Putem avea obiecte în obiecte în obiecte în obiecte etc. Să vedem acum o secvență completă:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function persoana (unNume, oVarsta, oAdresa) { this.nume = unNume this.varsta = oVarsta this.adresa = oAdresa } function adresa(oStrada, unNumar) { this.strada = oStrada this.numar = unNumar } var adresa = new adresa("Ferdinand I", 47) var elev = new persoana("Dragos", 17, adresa) alert(elev.nume + ": " + elev.varsta + " - " + elev.adresa.strada + " " + elev.adresa.numar) |
În linia 11 avem o variabilă pe care nu o folosim decât în linia 12; nu avem nevoie de ea. Putem scăpa de ea dacă ne gândim că un constructor returnează un obiect și în linia 12 avem nevoie de doar un astfel de obiect. Am putea rescrie liniile 11 și 12 astfel:
1 |
var elev = new persoana("Dragos", 17, new adresa("Ferdinand I", 48)) |
Putem avea obiecte în obiecte și dacă folosim construcțiile prezentate în episodul anterior. Am putea crea un obiect cu aceeași structură folosind:
1 2 3 4 5 6 7 8 |
var elev = { nume: "Dragos", varsta: "17", adresa: { strada: "Ferdinand I", numar: 47 } } |