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.