Getters și setters sunt funcții sau metode utilizate pentru a obține și seta valorile variabilelor. Conceptul de getter-setter este comun în programarea calculatoarelor: aproape toate limbajele de programare de nivel înalt vin cu un set de sintaxă pentru a implementa getteri și setteri, inclusiv JavaScipt.
Citește și: JavaScipt: 4 declarații JavaScript utile pe care ar trebui să le cunoașteți
În această postare, vom vedea ce sunt getters-setters și cum să le creăm și să le folosim în JavaScript.
- Getters-setters și încapsularea
- Crearea getterilor și setterilor
- 1. Cu metode
- 2. Cu cuvinte cheie
- Ce modalitate este mai bună?
- Prevenirea suprascrierii
- Operații în interiorul getterilor și setterilor
- Protejați datele cu getters și setters
- Date neprotejate
- 1. Block scoping
- 2. Funcția scoping
- 3. Protecția datelor fără scoping
- Când ar trebui să folosiți getteri și setteri?
Getters-setters și încapsularea
Ideea de getters și setters este întotdeauna menționată împreună cu încapsularea. Încapsularea poate fi înțeleasă în două moduri.
În primul rând, este stabilirea trio-ului date-getters-setters pentru a accesa și modifica acele date. Această definiție este utilă atunci când anumite operații, cum ar fi validarea, trebuie efectuate asupra datelor înainte de a le salva sau vizualiza – getters și setters oferă casa perfectă pentru aceasta.
În al doilea rând, există o definiție mai strictă, conform căreia încapsularea se face pentru a ascunde datele, pentru a le face inaccesibile din alt cod, cu excepția celor prin intermediul getters și setters. În acest fel nu ajungem să suprascriem din greșeală date importante cu un alt cod din program.
Crearea getterilor și setterilor
1. Cu metode
Din moment ce getters și setters sunt practic funcții care preiau/modifică o valoare, există mai multe moduri de a le crea și utiliza. Primul mod este:
var obj = { foo: 'this is the value of foo', getFoo: function() { return this.foo; }, setFoo: function(val) { this.foo = val; }}console.log(obj.getFoo());// "this is the value of foo"obj.setFoo('hello');console.log(obj.getFoo());// "hello"
Acesta este cel mai simplu mod de a crea getteri și setori. Există o proprietate foo
și există două metode: getFoo
și setFoo
pentru a returna și atribui o valoare acelei proprietăți.
2. Cu cuvinte cheie
Un mod mai „oficial” și mai robust de a crea getteri și setteri este prin utilizarea cuvintelor cheie get
și set
.
Pentru a crea un getter, plasați cuvântul cheie get
în fața unei declarații de funcție care va servi ca metodă getter și utilizați cuvântul cheie set
în același mod pentru a crea un setter. Sintaxa este următoarea:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}console.log(obj.foo);// "this is the value of foo"obj.foo = 'hello';console.log(obj.foo);// "hello"
Rețineți că datele pot fi stocate numai sub un nume de proprietate (fooVal
) care este diferit de numele metodelor getter-setter (foo
) deoarece o proprietate care deține getter-setterul nu poate deține și datele.
Ce modalitate este mai bună?
Dacă alegeți să creați getters și setters cu ajutorul cuvintelor cheie, puteți utiliza operatorul de atribuire pentru a seta datele și operatorul punct pentru a obține datele, în același mod în care ați accesa/stabili valoarea unei proprietăți obișnuite.
Dar, dacă alegeți primul mod de codificare a getterilor și setterilor, trebuie să apelați metodele setter și getter folosind sintaxa de apelare a funcțiilor, deoarece acestea sunt funcții tipice (nimic special ca cele create folosind cuvintele cheie get
și set
).
De asemenea, există posibilitatea să ajungeți să atribuiți din greșeală o altă valoare proprietăților care dețineau aceste metode getter-setter și să le pierdeți complet! Lucru de care nu trebuie să vă faceți griji în metoda de mai târziu.
Atunci, puteți vedea de ce am spus că a doua tehnică este mai robustă.
Prevenirea suprascrierii
Dacă dintr-un motiv oarecare preferați prima tehnică, faceți ca proprietățile care dețin metodele getter-setter să fie numai pentru citire, creându-le cu Object.defineProperties
. Proprietățile create prin Object.defineProperties
, Object.defineProperty
și Reflect.defineProperty
se configurează automat la writable: false
, ceea ce înseamnă read-only:
/* Overwrite prevention */var obj = { foo: 'this is the value of foo'};Object.defineProperties(obj, { 'getFoo': { value: function () { return this.foo; } }, 'setFoo': { value: function (val) { this.foo = val; } }});obj.getFoo = 66;// getFoo is not going to be overwritten!console.log(obj.getFoo());// "this is the value of foo"
Operații în interiorul getterilor și setterilor
După ce ați introdus getterii și setterii, puteți merge mai departe și efectua operații asupra datelor înainte de a le modifica sau returna.
În codul de mai jos, în funcția getter, datele sunt concatenate cu un șir de caractere înainte de a fi returnate, iar în funcția setter se efectuează o validare pentru a stabili dacă valoarea este sau nu un număr înainte de a actualiza n
.
var obj = { n: 67, get id() { return 'The ID is: ' + this.n; }, set id(val) { if (typeof val === 'number') this.n = val; }}console.log(obj.id);// "The ID is: 67"obj.id = 893;console.log(obj.id);// "The ID is: 893"obj.id = 'hello';console.log(obj.id);// "The ID is: 893"
Protejați datele cu getters și setters
Până acum, am acoperit utilizarea getters și setters în primul context al încapsulării. Să trecem la cel de-al doilea, și anume cum să ascundem datele de codul exterior cu ajutorul getterilor și setterilor.
Date neprotejate
Configurarea getterilor și setterilor nu înseamnă că datele pot fi accesate și modificate doar prin intermediul acelor metode. În exemplul următor, se modifică direct, fără a atinge metodele getter și setter:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}obj.fooVal = 'hello';console.log(obj.foo);// "hello"
Nu am folosit setterul, ci am modificat direct datele (fooVal
). Datele pe care le-am setat inițial în interiorul obj
au dispărut acum! Pentru a preveni ca acest lucru să se întâmple (accidental), aveți nevoie de o anumită protecție pentru datele dumneavoastră. Puteți adăuga acest lucru prin limitarea domeniului în care datele dvs. sunt disponibile. Puteți face acest lucru fie prin sfera de cuprindere a blocurilor, fie prin sfera de cuprindere a funcțiilor.
1. Block scoping
O modalitate este de a folosi un block scope în interiorul căruia datele vor fi definite folosind cuvântul cheie let
care limitează domeniul de aplicare la acel bloc.
Un block scope poate fi creat prin plasarea codului dvs. în interiorul unei perechi de acolade curly braces. Ori de câte ori creați o sferă de cuprindere a blocului, asigurați-vă că lăsați un comentariu deasupra acestuia cerând ca parantezele să fie lăsate în pace, astfel încât nimeni să nu elimine din greșeală parantezele crezând că sunt niște paranteze suplimentare redundante în cod sau să adauge o etichetă la sfera de cuprindere a blocului.
/* BLOCK SCOPE, leave the braces alone! */{let fooVal = 'this is the value of foo';var obj = { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect the fooVal inside the blockconsole.log(obj.foo);// "this is the value of foo"
Modificarea/creația de fooVal
în afara blocului nu va afecta fooVal
menționate în interiorul getters setters.
2. Funcția scoping
Modul cel mai comun de a proteja datele cu scoping este păstrarea datelor în interiorul unei funcții și returnarea unui obiect cu getteri și setori din acea funcție.
function myobj(){ var fooVal = 'this is the value of foo'; return { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect our original fooValvar obj = myobj();console.log(obj.foo);// "this is the value of foo"
Obiectul (cu foo()
getter-setterul foo()
în interiorul lui) returnat de funcția myobj()
este salvat în obj
, iar apoi obj
este folosit pentru a apela getterul și setterul.
3. Protecția datelor fără scoping
Există și o altă modalitate prin care puteți proteja datele de suprascriere fără a limita domeniul de aplicare. Logica din spatele acesteia este următoarea: cum poți modifica o bucată de date dacă nu știi cum se numește?
Dacă datele au un nume de variabilă/proprietate nu atât de ușor de reprodus, sunt șanse ca nimeni (nici măcar noi înșine) să nu ajungă să le suprascrie prin atribuirea unei valori la acel nume de variabilă/proprietate.
var obj = { s89274934764: 'this is the value of foo', get foo() { return this.s89274934764; }, set foo(val) { this.s89274934764 = val; }}console.log(obj.foo);// "this is the value of foo"
Vezi, acesta este un mod de a rezolva lucrurile. Deși numele pe care l-am ales eu nu este unul foarte bun, puteți folosi, de asemenea, valori sau simboluri aleatorii pentru a crea nume de proprietăți, așa cum este propus de Derick Bailey în această postare pe blog. Scopul principal este de a păstra datele ascunse de alte coduri și de a lăsa o pereche getter-setter să le acceseze/actualizeze.
Când ar trebui să folosiți getteri și setteri?
Acum vine marea întrebare: începeți să atribuiți getteri și setteri la toate datele dvs. acum?
Dacă ascundeți datele, atunci nu există altă alegere.
Dar dacă datele dvs. pot fi văzute de alt cod este în regulă, mai trebuie să folosiți getters setters doar pentru a le grupa cu codul care efectuează unele operații asupra lor? Eu aș spune că da. Codul se adună foarte repede. Crearea unor microunități de date individuale cu propriul getter-setter vă oferă o anumită independență pentru a lucra asupra datelor respective fără a afecta alte părți ale codului.
Citește și: 10 motive pentru care aveți nevoie de optimizarea codului
.