Er zijn twee soorten objecteigenschappen.

De eerste soort is data-eigenschappen. We weten al hoe we daarmee moeten werken. Alle eigenschappen die we tot nu toe hebben gebruikt, waren data-eigenschappen.

De tweede soort eigenschappen is iets nieuws. Het zijn accessor properties. Het zijn in wezen functies die worden uitgevoerd bij het krijgen en instellen van een waarde, maar er voor een externe code uitzien als gewone properties.

Getters en setters

Accessor properties worden weergegeven door “getter” en “setter” methoden. In een object letterlijk worden ze aangeduid met get en 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 }};

De getter werkt wanneer obj.propName wordt gelezen, de setter – wanneer het wordt toegewezen.

We hebben bijvoorbeeld een user object met name en surname:

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

Nu willen we een fullName eigenschap toevoegen, dat moet "John Smith" zijn. Natuurlijk willen we geen bestaande informatie copy-pasten, dus kunnen we het implementeren als een accessor:

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

Van de buitenkant ziet een accessor property eruit als een gewone. Dat is het idee van accessor properties. We roepen user.fullName niet aan als een functie, we lezen hem normaal: de getter loopt achter de schermen.

Zoals het er nu uitziet, heeft fullName alleen een getter. Als we user.fullName= proberen toe te wijzen, zal er een fout optreden:

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

Laten we dit oplossen door een setter voor user.fullName toe te voegen:

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

Als resultaat hebben we een “virtuele” eigenschap fullName. Het is leesbaar en beschrijfbaar.

Accessor descriptors

Descriptors voor accessor properties zijn anders dan die voor data properties.

Voor accessor properties is er geen value of writable, maar in plaats daarvan zijn er get en set functies.

Dat wil zeggen, een accessor descriptor kan hebben:

  • get – een functie zonder argumenten, die werkt wanneer een eigenschap wordt gelezen,
  • set – een functie met één argument, die wordt aangeroepen wanneer de eigenschap wordt ingesteld,
  • enumerable – hetzelfde als voor data-eigenschappen,
  • configurable – hetzelfde als voor data-eigenschappen.

Om bijvoorbeeld een accessor fullName met defineProperty te maken, kunnen we een descriptor met get en set doorgeven:

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

Merk op dat een eigenschap ofwel een accessor kan zijn (heeft get/set methoden) of een data eigenschap (heeft een value), niet beide.

Als we proberen om zowel get en value in dezelfde descriptor op te geven, zal er een fout optreden:

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

Slimmere getters/setters

Getters/setters kunnen worden gebruikt als wrappers over “echte” property-waarden om meer controle te krijgen over operaties ermee.

Bijvoorbeeld, als we te korte namen voor user willen verbieden, kunnen we een setter name hebben en de waarde in een aparte property _name houden:

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...

Dus, de naam is opgeslagen in _name property, en de toegang wordt gedaan via getter en setter.

Technisch gezien kan externe code de naam direct benaderen door user._name te gebruiken. Maar er is een algemeen bekende conventie dat eigenschappen die beginnen met een underscore "_" intern zijn en niet mogen worden aangeraakt van buiten het object.

Gebruik voor compatibiliteit

Een van de grote toepassingen van accessors is dat ze het mogelijk maken om op elk moment controle te nemen over een “gewone” data property door deze te vervangen door een getter en een setter en het gedrag ervan aan te passen.

Stel je voor dat we gebruikersobjecten gaan implementeren met behulp van data properties name en age:

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

…Maar vroeg of laat kunnen dingen veranderen. In plaats van age kunnen we besluiten om birthday op te slaan, omdat dat nauwkeuriger en handiger is:

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

Nu wat te doen met de oude code die nog steeds age property gebruikt?

We kunnen proberen om al die plaatsen te vinden en ze te repareren, maar dat kost tijd en kan moeilijk zijn om te doen als die code door veel andere mensen wordt gebruikt. En bovendien, age is een leuk ding om in user te hebben, toch?

Laten we het houden.

Een getter toevoegen voor age lost het probleem op:

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

Nu werkt de oude code ook en hebben we een mooie extra eigenschap.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.