December 23, 2013
Per la maggior parte, in JavaScript, quello che vedi è quello che ottieni. Un valore è un valore; non ci sono trucchi. A volte, però, vuoi un valore che si basa su altri valori: il nome completo di qualcuno, per esempio, è una concatenazione del suo nome e cognome. Se avete un oggetto person
, e volete che gli utenti di quell’oggetto siano in grado di impostare il nome completo, il nome o il cognome, e vedere il cambiamento immediatamente riflesso negli altri valori, lo costruite convenzionalmente con le funzioni:
person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith
Ma questo è brutto, e richiede che agli utenti del vostro oggetto importi che le proprietà siano correlate; in un esempio più complesso, questo potrebbe non essere così ovvio come con i nomi. Fortunatamente, c’è un modo migliore, aggiunto in ECMAScript 5.
Incontra i getter e i setter.
Come
Facciamo l’oggetto persona. Vogliamo essere in grado di impostare il nome, il cognome o il nome completo, e fargli aggiornare gli altri due automaticamente.
var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words || ''; this.lastName = words || ''; }}person.fullName = 'Jack Franklin';console.log(person.firstName); // Jackconsole.log(person.lastName) // Franklin
Quindi cosa sta succedendo qui?
Le parole chiave get e set sono importanti. Seguono la proprietà a cui si riferiscono (fullName
) e un corpo di funzione che definisce il comportamento quando si accede alla proprietà (name = person.fullName
) o la si modifica (person.fullName = 'Some Name'
).
Queste due parole chiave definiscono funzioni accessorie: un getter e un setter per la proprietà fullName
. Quando si accede alla proprietà, viene utilizzato il valore di ritorno del getter. Quando viene impostato un valore, il setter viene chiamato e gli viene passato il valore che è stato impostato. Sta a te decidere cosa fare con quel valore, ma ciò che viene restituito dal setter è il valore che è stato passato – quindi non hai bisogno di restituire nulla.
Il modo ufficiale: Object.defineProperty
Oltre al metodo inline per dichiarare getter e setter, può anche essere fatto più esplicitamente tramite Object.defineProperty
(MDN Documentation). Questo metodo prende tre argomenti. Il primo è l’oggetto a cui aggiungere la proprietà, il secondo è il nome della proprietà e il terzo è un oggetto che descrive la proprietà (noto come descrittore della proprietà). Ecco un esempio che replica l’esempio precedente:
var person = { firstName: 'Jimmy', lastName: 'Smith'};Object.defineProperty(person, 'fullName', {get: function() {return firstName + ' ' + lastName;},set: function(name) {var words = name.split(' ');this.firstName = words || '';this.lastName = words || '';}});
Il vantaggio qui non è immediatamente evidente. Oltre a poter aggiungere proprietà dopo aver creato l’oggetto iniziale, c’è un reale beneficio?
Quando si definisce una proprietà in questo modo, si può fare molto di più che definire un setter o un getter. Puoi anche passare le seguenti chiavi:
-
configurable
(false
di default): se questo è vero, la configurazione della proprietà sarà modificabile in futuro. -
enumerable
(false
di default): se è vero, la proprietà apparirà quando si fa il looping sull’oggetto (for (var key in obj)
).
Possiamo anche definire proprietà che non hanno getter o setter espliciti:
Object.defineProperty(person, 'age', { value: 42});
Questo creerà person.age
, e lo imposterà al valore 42. È importante notare che questa proprietà non è scrivibile. Chiamare person.age = 99
non avrà alcun effetto. In questo modo potete creare proprietà di sola lettura. Se una proprietà ha una chiave value
impostata, non può avere un getter o un setter. Le proprietà possono avere valori o accessi, non entrambi.
Non solo, ma poiché la proprietà enumerable
è di default a false
, questa proprietà non apparirà quando faremo un loop sulle chiavi dell’oggetto.
Se volessimo rendere una proprietà scrivibile, dovremmo impostare la proprietà writable
:
Object.defineProperty(person, 'age', { value: 42, writable: true});
Ora, person.age = 99;
avrà l’effetto desiderato.
Overuse
Ricorda: solo perché una funzione esiste, non deve essere usata sempre. Getters e Setters hanno i loro casi d’uso, ma non esagerate, o molto probabilmente vi ritroverete con un design che confonde chi interagisce con i vostri oggetti. Usati con attenzione, sono molto potenti. Ma da un grande potere derivano grandi responsabilità.
Supporto browser?
IE9 e superiori hanno pieno supporto per Object.defineProperty
, insieme a Safari 5+, Firefox 4+, Chrome 5+ e Opera 12+. Se stai lavorando con Node.js, c’è pieno supporto. Non amate Node?!
Questo articolo è stato scritto insieme a Tom Ashworth. Grazie a Tom per tutto il suo aiuto nel metterlo insieme.
Se ti è piaciuto questo post, iscriviti alla newsletter per essere aggiornato sui nuovi contenuti 😎.
Tutti i post del blog
Se ti è piaciuto questo post, assicurati di seguirmi su Twitter.