Încapsularea datelor în JavaScript: getters și setters
Când se construiesc aplicații JavaScript mai mari, apare în curând necesitatea de a le împărți în module legate prin contracte clare. Când vine vorba de mentenanța pe termen lung, trebuie prevăzută o modalitate de a păstra compatibilitatea la schimbările de interfață. Pentru a face acest lucru, limbajele orientate pe obiecte se bazează pe încapsulare sau pe informații care ascund detaliile de implementare de la utilizatorii unei bucăți de cod, astfel încât aceasta să se poată schimba fără a afecta clienții.
În această postare vom vorbi despre Încapsularea datelor în JavaScript. Mai exact, vom vorbi despre încapsularea proprietăților în spatele getters și setters.
Câteva concepte
JavaScript a fost proiectat având la bază principiul de tipare a raței:
“Când văd o pasăre care merge ca o rață și înoată ca o rață și cotcodăcește ca o rață, numesc acea pasăre rață.”
Cu alte cuvinte, în esența sa, JavaScript este un limbaj orientat pe obiecte, dar, spre deosebire de alte limbaje orientate pe obiecte, cum ar fi C# și Java, în JavaScript clasele sunt cetățeni de mâna a doua, iar interfețele nu există. Obiectele nu au tipuri și, prin urmare, trebuie scris cod pentru a crea interfețe cu obiecte care au anumite proprietăți și metode. Contractele dintre module se bazează pe un set de metode și proprietăți presupuse a fi prezente în obiectele schimbate.
Încapsularea este fundamentală în programarea orientată pe obiecte. Aceasta înseamnă că un anumit obiect ar trebui să fie capabil să ascundă detaliile funcționării sale interne, astfel încât alte obiecte care interacționează cu acesta să nu depindă de detaliile implementării sale, ci de o interfață convenită la nivel înalt. Acest lucru face viața dezvoltatorilor mai simplă, atât din partea furnizorului, cât și din partea utilizatorilor, deoarece ambii știu că detaliile de implementare se pot schimba fără a rupe obiectele clientului.
De ce expunerea datelor pe o interfață este o idee proastă
În JavaScript un obiect este pur și simplu un dicționar de nume de proprietăți mapate pe valori, când valoarea unei proprietăți este o funcție, o numim metodă. Interfața dintre metode este setul de proprietăți pe care codul client se așteaptă să le găsească pe un obiect.
Dacă un obiect expune doar metode, este ușor de făcut ca interfața să evolueze odată cu timpul. O metodă își poate verifica parametrii și poate reacționa în consecință. De asemenea, este posibil să se creeze noi metode care să ofere noi caracteristici. Și, susținerea celor vechi care adaptează vechiul comportament la cel nou. Cu proprietățile nu este posibil să se facă acest lucru.
În cazul proprietăților, menținerea unei interfețe stabile nu este ușoară. Pentru că, în mod implicit, codul client se poate referi la proprietăți inexistente ale unui obiect. Scrierea la o proprietate inexistentă o va crea. Citirea acesteia va returna nedefinit. Codul client nu va fi capabil să detecteze rapid dacă utilizează proprietăți depreciate. Mai rău, eroarea se poate propaga în alte părți ale codului, făcând detectarea problemei mai dificilă.
Apif-ul standard JavaScript are metode care pot fi utile pentru a evita astfel de probleme: este posibil să înghețe sau să sigileze un obiect, astfel încât numele proprietăților care nu sunt suportate să nu poată fi utilizate.
Getter/Setters pentru a salva ziua
Ascunderea detaliilor de implementare a metodelor este ușoară.
Getters/setters au fost introduși oficial în limbaj în ECMAScript 5.1 (ECMA-262). Aceștia sunt în prezent suportați în toate browserele principale de desktop și mobile.
Ideea de bază este că se adaugă sintaxa pentru a defini proprietățile de accesor ca metode, în loc de simple proprietăți de date. Un getter este definit prin cuvântul cheie get urmat de o funcție numită după proprietate, care nu ia argumente și returnează valoarea proprietății. Un setter este definit prin cuvântul cheie set urmat de o funcție numită după proprietate, care ia ca parametru noua valoare a proprietății.
Exemplul următor ilustrează un getter și un setter utilizate pentru a defini o proprietate accesor numită prop:
var obj = { v: 0, get prop() { return this.v; }, set prop(newValue) { this.v = newValue; }};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);
Scoatere:
042
Se poate crea, de asemenea, o proprietate numai pentru citire prin definirea unui getter fără setter:
var obj = { get prop() { return -1; },};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);
Output:
-1-1
Orice proprietate poate fi apoi înlocuită cu câteva metode care, în cazul în care implementarea unei clase se schimbă, pot reacționa la aceasta și pot adapta comportamentul clasei. În cel mai rău caz, poate ridica o excepție pentru a anunța utilizatorul că proprietatea este depreciată și nu ar trebui să mai fie folosită.
Lectură suplimentară
Sper că ați învățat ceva nou despre proiectarea de aplicații evolutive complexe în JavaScript în această postare. În această secțiune, vă voi oferi câteva linkuri către linkuri care conțin mai multe informații.
Articole introductive
Această secțiune aduce câteva articole scurte care prezintă conceptele și oferă o imagine de ansamblu a API-urilor aferente:
- Property getters and setters.
- JavaScript Getters and Setters
Articole mai avansate
Aceste articole vorbesc despre subiecte conexe mai avansate, cum ar fi problemele aduse de getters și setters, și cum să le rezolve.
- De ce getters/setters este o idee proastă în JavaScript
- Încapsularea datelor în JavaScript
Documentație de referință
În cele din urmă, pentru a stăpâni cu adevărat subiectul, link-urile către documentația mozilla a API-urilor aferente:
- get și set permit definirea getters și setters. Dincolo de ceea ce am explicat aici. Există și alte caracteristici interesante, cum ar fi suportul pentru nume de proprietăți generate dinamic și utilizarea getterilor și setterilor pentru a simula setarea/obținerea de valori într-o matrice
- seal și freeze permit să se controleze ce proprietăți ale unui obiect sunt sau nu modificabile și cum. Acestea pot fi utilizate pentru a evita ca clienții să folosească părți depreciate din API-ul unui obiect.
- defineProperty permite definirea getterilor și setterilor și, în același timp, un control mai mare asupra modului în care aceste proprietăți sunt văzute de către clienți și asupra modului în care acestea sunt modificabile.