Există două tipuri de proprietăți ale obiectelor.

Primul tip sunt proprietățile de date. Știm deja cum să lucrăm cu ele. Toate proprietățile pe care le-am folosit până acum au fost proprietăți de date.

Cel de-al doilea tip de proprietăți este ceva nou. Este vorba de proprietățile accesor. Acestea sunt, în esență, funcții care se execută la obținerea și setarea unei valori, dar care arată ca niște proprietăți obișnuite pentru un cod extern.

Getters și setters

Proprietățile accesor sunt reprezentate de metodele „getter” și „setter”. Într-un literal de obiect, acestea sunt notate prin get și set:

let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};

Letera de obținere funcționează atunci când obj.propName este citită, iar cea de setare – atunci când este atribuită.

De exemplu, avem un obiect user cu name și surname:

let user = { name: "John", surname: "Smith"};

Acum dorim să adăugăm o proprietate fullName, care ar trebui să fie "John Smith". Desigur, nu dorim să copiem informațiile existente, așa că o putem implementa ca accesor:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John Smith

Din exterior, o proprietate accesor arată ca una obișnuită. Aceasta este ideea proprietăților accesor. Nu apelăm user.fullName ca o funcție, ci o citim în mod normal: getterul se execută în spatele scenei.

Deocamdată, fullName are doar un getter. Dacă încercăm să atribuim user.fullName=, se va produce o eroare:

let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)

Să rezolvăm această problemă adăugând un setter pentru user.fullName:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // Cooper

Ca rezultat, avem o proprietate „virtuală” fullName. Aceasta poate fi citită și scrisă.

Descriptorii accesoriului

Descriptorii pentru proprietățile accesor sunt diferiți de cei pentru proprietățile de date.

Pentru proprietățile accesor, nu există value sau writable, dar în schimb există funcțiile get și set.

Atunci, un descriptor de accesor poate avea:

  • get – o funcție fără argumente, care funcționează atunci când o proprietate este citită,
  • set – o funcție cu un singur argument, care este apelată atunci când proprietatea este setată,
  • enumerable – la fel ca în cazul proprietăților de date,
  • configurable – la fel ca în cazul proprietăților de date.

De exemplu, pentru a crea un accesor fullName cu defineProperty, putem trece un descriptor cu get și set:

let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

Rețineți că o proprietate poate fi fie un accesor (are metode get/set), fie o proprietate de date (are un value), nu ambele.

Dacă încercăm să furnizăm atât get, cât și value în același descriptor, se va produce o eroare:

// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});

Câștigători/setori mai inteligenți

Câștigătorii/setorii pot fi utilizați ca învelișuri peste valorile „reale” ale proprietăților pentru a obține un control mai mare asupra operațiilor cu acestea.

De exemplu, dacă dorim să interzicem numele prea scurte pentru user, putem avea un setter name și să păstrăm valoarea într-o proprietate separată _name:

let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...

Așa, numele este stocat în proprietatea _name, iar accesul se face prin getter și setter.

Tehnic, codul extern este capabil să acceseze numele direct prin utilizarea user._name. Dar există o convenție larg cunoscută conform căreia proprietățile care încep cu un caracter de subliniere "_" sunt interne și nu ar trebui să fie atinse din afara obiectului.

Utilizare pentru compatibilitate

Una dintre marile utilizări ale accesorilor este că aceștia permit preluarea controlului asupra unei proprietăți de date „obișnuite” în orice moment prin înlocuirea acesteia cu un getter și un setter și modificarea comportamentului acesteia.

Imaginați-vă că am început să implementăm obiecte utilizator folosind proprietăți de date name și age:

function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

…Dar mai devreme sau mai târziu, lucrurile se pot schimba. În loc de age putem decide să stocăm birthday, pentru că este mai precis și mai convenabil:

function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));

Și acum ce ne facem cu codul vechi care încă folosește proprietatea age?

Potențiam încerca să găsim toate aceste locuri și să le reparăm, dar acest lucru necesită timp și poate fi greu de realizat dacă acel cod este folosit de mulți alți oameni. Și, în plus, age este un lucru plăcut de avut în user, nu?

Să o păstrăm.

Aducerea unui getter pentru age rezolvă problema:

function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the age

Acum și vechiul cod funcționează și noi avem o proprietate suplimentară frumoasă.

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.