Het concept van getters en setters is vrij gangbaar in de populaire talen die tegenwoordig worden gebruikt. Hebt u er ooit voor moeten zorgen dat een bepaalde actie altijd wordt uitgevoerd voor of na het toewijzen van een waarde aan een eigenschap van een object? Dit is mogelijk met setters. Je kunt ook eender welke bewerking uitvoeren bij het ophalen van de waarde van een eigenschap door getters te gebruiken (b.v. formatteren). Eén manier om getters en setters te implementeren is door methodes te gebruiken, zoals dat in Java wordt gedaan. Echter, sinds ES5, hebben we een veel eleganter en praktischer alternatief beschikbaar door gebruik te maken van de get
en set
syntaxis. Laten we beginnen!
In JavaScript zijn objecten verzamelingen van eigenschappen en je hebt ze waarschijnlijk al eerder gebruikt om gegevens op te slaan. Een eigenschap is gewoon een sleutel-waarde paar. Hier is een voorbeeld:
const options = {
timeout: 5000,
retries: 3,
ip: '192.168.1.20',
port: 4001,
};
Eigenschappen in een object kunnen eenvoudig worden ingesteld en gelezen, zoals dit:
artist.name = 'Carice Anouk van Houten';console.log(artist.name); //=> "Carice Anouk van Houten"
Alweer, dit is zeker niets nieuws als je al enige ervaring met JavaScript had. Om een onderscheid te maken tussen deze gewone eigenschappen en het andere type dat we in dit artikel zullen zien, kunnen we de eerstgenoemde “data properties” noemen.
Getters en setters: de oude manier
Vóór ES5 was de enige manier waarop we getters en setters moesten implementeren het gebruik van gewone methoden. Zoals in het voorbeeld hieronder:
Ik heb het function keyword in het bovenstaande voorbeeld behouden om duidelijk te maken dat er hier geen sprake is van magie. Alleen normale methoden. We hadden hetzelfde effect kunnen bereiken door de methode-definitie syntaxis van ES2015 te gebruiken, die korter en iets eleganter is:
const config = {
_port: 4001,
setPort(port) {
// ...
},
getPort() {
// ...
},
};
Het gebruik van dit soort getters en setters is vrij eenvoudig:
config.setPort(8080);
console.log(config.getPort()); //=> 8080
Het verhogen van een waarde zou als volgt worden gedaan:
item.setQuantity(item.getQuantity() + 1);
Wat er erg omslachtig uitziet als je het vergelijkt met het direct verhogen van een eigenschap:
item.quantity += 1;
// Or even simpler...
item.quantity++;
Getters en setters: waarom?
In OO talen zoals Java, C++ of C# is het mogelijk om de leden van een klasse op verschillende niveaus bloot te stellen door gebruik te maken van access modifiers (zoals public
, protected
en private
). Dit dient om een belangrijk concept van OOP te implementeren, genaamd data hiding.
Een zeer veel voorkomend gebruik van getters en setters in deze talen is om leden van een klasse op een veilige manier bloot te stellen. Je zou bijvoorbeeld alleen een getter kunnen implementeren om er zeker van te zijn dat een private member nooit van buiten de klasse kan worden veranderd. Een setter op zijn beurt kan worden gebruikt om de geleverde waarden te valideren en de integriteit van instanties van die klasse te bewaren.
Hoe zit het met JavaScript?
In JavaScript liggen de zaken een beetje anders. Op het moment van schrijven van dit artikel zijn er nog geen access modifiers in JS. De beste optie die we momenteel hebben is het gebruik van symbolen, die ons een zekere mate van privacy geven als ze als sleutel worden gebruikt. Sommige mensen houden daar echter niet van. Vooral omdat het gemakkelijk is om deze privacy te doorbreken door gebruik te maken van reflectie.
Ik zie dit niet als een echt probleem, omdat talen die reflectie kennen, programmeurs meestal toestaan om de inkapseling op een of andere manier te doorbreken. Het probleem dat ik zie met symbolen is dat ze niet zo praktisch in het gebruik zijn als een eenvoudige private accessor zou zijn. Het goede nieuws is dat een EcmaScript voorstel dat private velden bevat al in fase 3 zit. En ze zullen immuun zijn voor elke vorm van reflectie.
In het voorbeeld van de vorige sectie hebben we geen symbolen of een andere truc gebruikt om een private member te simuleren. De _port
eigenschap heeft op zich niets bijzonders. Het is gewoon een gewone data property en kan als zodanig direct worden ingesteld of gelezen, volledig voorbijgaand aan de getter en setter die we hebben gemaakt.
Omdat het uitleggen van symbolen en andere inkapselingstechnieken niet het doel is van dit artikel, implementeer ik geen private properties in de codevoorbeelden. Bedenk wel dat getters en setters vaak (maar niet altijd) worden gebruikt om toegang te verschaffen tot ingekapselde leden. Tenslotte, let op de underscore voor de naam van de _port
eigenschap. Het is een de facto conventie onder JS ontwikkelaars, die aangeeft dat een bepaalde eigenschap prive is en niet mag worden benaderd van buiten de klasse waarin het is gedefinieerd.
Getters en setters: de nieuwe manier met accessor properties
Accessor properties zijn de moderne manier om getters en setters te definiëren. Wat hun definitie betreft, zijn ze niet veel verschillend van ons vorig voorbeeld, maar het is hun gebruik dat echt anders is. Laten we eerst onze code aanpassen om accessor properties te gebruiken:
Zoals u kunt zien, zijn er slechts twee dingen veranderd in dit voorbeeld:
- In plaats van
setPort
engetPort
methoden te gebruiken. We gebruiken deset
enget
syntaxis samen met een enkele naam voor de eigenschap (“port
“, in dit voorbeeld) - We hebben het
function
sleutelwoord hier niet nodig. Sterker nog, als je het probeert te gebruiken, krijg je een SyntaxError.
Hoe gebruik je accessor properties?
Als de definitie van een accessor property veel lijkt op hoe we old school getters en setters maken, heeft het gebruik ervan geen zichtbaar verschil met hoe we reguliere data properties gebruiken:
config.port = 8080;
console.log(config.port); //=> 8080
En hier is hoe een increment expression eruit ziet:
timer.seconds += 1;
Ja. Zo simpel is het. =)
Conclusie
- Objecten zijn verzamelingen van eigenschappen
- Een gewone eigenschap is gewoon een sleutel-waarde paar en kan een “data property” worden genoemd”
- Je kunt getters en setters implementeren als normale methoden. Dit is de “oude school” manier om dit te doen in JavaScript.
- Getters en setters worden normaal gebruikt om toegang te geven tot private leden van een klasse.
- Accessor properties kunnen zich precies zo gedragen als de oude-reguliere-method-based getters en setters, maar hun gebruik is ontworpen om ze te laten lijken op data properties.
Wil je JavaScript onder de knie krijgen? Ik raad je Mosh’s JavaScript cursussen ten zeerste aan.
En als je dit artikel leuk vond, deel het dan ook met anderen!