Lucrul cu obiecte e mai degrabă specific limbajelor din categoria “POO” sau programare orientată pe obiect, unde baza pentru organizarea codului este clasa.
JavaScript e un limbaj care e cunoscut mai degrabă ca un limbaj “funcțional”, adică organizarea codului se face mai degrabă sub formă de funcții, în loc de clase ca într-un limbaj orientat pe obiect.
Asta nu e singura diferență dintre cele 2 paradigme, mai există și altele mai puțin evidente, dar despre asta cu altă ocazie.
1. Obiectele în JavaScript
Totuși dacă ai scris vreodată cod de JavaScript e imposibil să nu te fi folosit de obiecte, chiar dacă poate nu ți-ai dat seama de asta la momentul respectiv.
Dacă te-ai folosit vreodată de oricare din funcțiile utilitare pentru alterarea unui string
cum ar fi myString.substring()
sau myString.concat()
, ei bine află că fără să-ți dai seama te-ai folosit de un obiect de tipul String
.
2. Modurile în care se pot crea obiecte
Există mai multe tipuri prin care putem obține un obiect în JavaScript, acestea fiind:
a. Obiect literal
Care se obține prin specificarea directă a structurii obiectului folosind {}
.
const person = {
name: "Bogdan"
present: function() {
console.log(`Salut, numele meu este ${this.name}`);
}
}
Codul de mai sus îți va crea un obiect cu o proprietate de tip string
denumită name
și inițializată cu valoarea “Bogdan” și o metodă (funcțiile declarate în interiorul obiectelor poartă numele de metode) denumită present()
.
b. Funcție constructor
Folosim o funcție declarată împreună cu cuvântul cheie new
pentru a obține un obiect conform cu structura funcției respective, asta însemnând toți membri, atât proprietăți cât și funcții.
function Person(name) {
name;
this.name = name;
present() {
console.log(`Salut, numele meu este ${this.name}`);
}
}
const person = new Person("Bogdan");
Ai grijă când intenționezi să declari o funcție ca și constructor, pentru că prin convenție, ea trebuie să fie denumită cu literă mare și să denote tipul obiectului obținut în urma apelării funcției. Vezi Person
nu getPerson
sau createPerson
.
3. Clase și programare orientată pe obiect în JavaScript
A treia modalitate și cea mai apropiată de abordarea folosită în limbajele de programare orientate pe obiect e folosind o clasă.
În momentul în care folosim sintaxa de tip clasă, parametri necesari construcției obiectului vor fi pasați printr-o metodă (funcțiile declarate în interiorul claselor poartă de asemenea numele de metode) dedicată denumită cosntructor și al cărui cuvânt cheie este de asemenea constructor
.
class Person {
name;
constructor(name) {
this.name = name;
}
present() {
console.log(`Salut, numele meu este ${this.name}`);
}
}
const person = new Person("Bogdan");
Încapsularea
Folosirea sintaxei de tip clasă aduce după sine și alte funcționalități similare claselor în limbajele orientate pe obiect, cum ar fi faptul că îți poți încapsula proprietățile făcându-le private. Adică ele devin inacesibile și deci necunoscute codului exterior.
class Person {
name;
dateOfBirth;
#age;
constructor(name, dateOfBirth) {
this.name = name;
this.dateOfBirth = dateOfBirth;
// mai e nevoie să ții cont și de lună aici, dar
// pentru simplitate o să lăsăm implementarea așa :)
this.#age = new Date().getFullYear() - dateOfBirth.getFullYear();
}
present() {
console.log(`Salut, numele meu este ${this.name}`);
}
isAdult() {
return this.#age >= 18;
}
}
Aceeași sintaxă folosită pentru #age
se poate folosi și cu metode pentru a le face private.
Execuția codului de mai sus va duce la următorul lanț de acțiuni:
- un nou obiect va fi creat
- se va lega
this
de obiectul nou creat pentru a putea fi folosit în constructor - se va executa integral codul din constructor
- se va returna obiectul creat
Există și posibilitatea de a omite declararea specifică a unui constructor și unul implicit va fi generat pentru tine automat.
class Person {
name;
present() {
console.log(`Salut, numele meu este ${this.name}`);
}
}
const person = new Person("Bogdan");
Moștenirea
Pe lângă partea de încapsulare, clasele în JavaScript aduc și posibilitatea extinderii lor prin procedeul numit în programarea orientată pe obiect moștenire.
class Employee extends Person {
role;
constructor(name, dateOfBirth, role) {
super(name, dateOfBirth);
this.role = role;
}
present() {
console.log(`Salut, numele meu este ${this.name} și sunt ${this.role}`);
}
}
const employee = new Employee(
"Alex",
new Date("01-01-1999"),
"Software Engineer"
);
employee.present(); //Salut, numele meu este Alex și sunt Software Engineer
Folosind cuvântul cheie extends
vom extinde funcționalitățile clasei Person
în Employee
și vom introduce de asemenea proprietatea role
pentru rolul angajatului în cadrul unei companii.
Acordă atenție modului în care sunt pasate argumente clasei părinte, folosind sintaxa super()
, astfel transmitem valorile proprietăților moștenite.
De asemenea acum putem și suprascrie metodele moștenite de la clasa părinte, simplu prin redeclararea lor mai jos pe lanțul ierarhic.
Acum în implementarea de present()
din Employee
am introdus și noua proprietate adăugată, role
.
Concluzie
Clasele au adus astfel în JavaScript câteva din comportamentele limbajelor orientate pe obiect, iar dacă ai programat în trecut în Java sau C#, cu siguranță o astfel de sintaxă îți este familiară.
Ce mai vreau să ții cont e faptul că “sub capotă” modul în care este obținută moștenirea e tot prin mecanismele native de prototipare ale JavaScript.
Adică atunci când se obține un obiect dintr-o clasă care extinde alte clase, “moștenirea” e obținută prin crearea câte unui obiect individual pentru fiecare nivel ierarhic în parte care joacă rol de prototip pentru următorul nivel din lanț.
Contrar a ceea ce se întâmplă în limbajele nativ orientate pe obiect, unde se obține un singur obiect compus din toate metodele și proprietățile de pe lanțul ierarhic.