Data-encapsulatie in JavaScript: getters en setters

Data-encapsulatie in JavaScript: getters en setters

Bij het bouwen van grotere JavaScript-applicaties ontstaat al snel de behoefte om deze op te splitsen in modules die door duidelijke contracten met elkaar zijn verbonden. Als het gaat om onderhoud op lange termijn, moet er een manier zijn om compatibiliteit te behouden bij interface veranderingen. Om dat te doen, vertrouwen object georiënteerde talen op inkapseling of informatie die implementatie details verbergt voor gebruikers van een stuk code, zodat het kan veranderen zonder invloed op clients.

In dit bericht zullen we praten over Data inkapseling in JavaScript. Meer specifiek zullen we het hebben over het inkapselen van eigenschappen achter getters en setters.

Enkele concepten

JavaScript is ontworpen met het principe van duck typing als basis:

“Als ik een vogel zie die loopt als een eend en zwemt als een eend en kwaakt als een eend, noem ik die vogel een eend.”

Met andere woorden, JavaScript is in de kern een objectgeoriënteerde taal, maar anders dan andere objectgeoriënteerde talen zoals C# en Java, zijn in JavaScript klassen tweederangsburgers en bestaan interfaces niet. Objecten hebben geen types, en dus moet code geschreven worden om een interface te maken met objecten die bepaalde eigenschappen en methodes hebben. Contracten tussen modules zijn gebaseerd op een reeks methoden en eigenschappen waarvan wordt aangenomen dat ze aanwezig zijn in de uitgewisselde objecten.

Encapsulatie is fundamenteel in object georiënteerd programmeren. Het betekent dat een bepaald object in staat moet zijn om details van zijn innerlijke werking te verbergen, zodat andere objecten die ermee interageren niet afhankelijk zijn van details van de implementatie ervan, maar van een op hoog niveau overeengekomen interface. Dit maakt het leven van ontwikkelaars eenvoudiger, zowel aan de kant van de provider als aan de kant van de gebruikers, omdat beiden weten dat de implementatiedetails kunnen veranderen zonder client-objecten te breken.

Waarom het blootstellen van gegevens op een interface een slecht idee is

In JavaScript is een object eenvoudigweg een woordenboek van namen van eigenschappen die zijn toegewezen aan waarden, wanneer de waarde van een eigenschap een functie is, noemen we het een methode. De interface tussen methoden is de verzameling eigenschappen die de client code verwacht te vinden op een object.

Als een object alleen methoden blootstelt, is het gemakkelijk om de interface mee te laten evolueren met de tijd. Een methode kan haar parameters controleren en dienovereenkomstig reageren. Het is ook mogelijk om nieuwe methoden te creëren die nieuwe functies bieden. En oude methoden te ondersteunen die het oude gedrag aanpassen aan het nieuwe. Met eigenschappen is dat niet mogelijk.

In het geval van eigenschappen is het niet eenvoudig om een stabiele interface te houden. Want, standaard kan client code verwijzen naar niet bestaande eigenschappen van een object. Schrijven naar een niet-bestaande eigenschap zal het creëren. Lezen ervan zal ongedefinieerd teruggeven. Client code zal niet in staat zijn snel te detecteren of het deprecated eigenschappen gebruikt. Erger nog, de fout kan zich verspreiden naar andere delen van de code, waardoor detectie van het probleem moeilijker wordt.

De standaard JavaScript API heeft methoden die nuttig kunnen zijn bij het vermijden van dergelijke problemen: het is mogelijk om een object te bevriezen of te verzegelen, zodat niet-ondersteunde namen van eigenschappen niet kunnen worden gebruikt.

Getter/Setters to save the day

Het verbergen van de details van methode implementatie is eenvoudig.

Getters/setters zijn officieel geïntroduceerd in de taal in ECMAScript 5.1 (ECMA-262). Ze worden momenteel ondersteund in alle belangrijke desktop- en mobiele browsers.

Het basisidee is dat het syntaxis toevoegt om accessor properties te definiëren als methoden, in plaats van eenvoudige data-eigenschappen. Een getter wordt gedefinieerd door het trefwoord get gevolgd door een functie genoemd naar de eigenschap, die geen argumenten neemt en de waarde van de eigenschap teruggeeft. Een setter wordt gedefinieerd door het keyword set gevolgd door een functie genoemd naar de property, die de nieuwe waarde van de property als parameter neemt.

Het volgende voorbeeld illustreert een getter en een setter die worden gebruikt om een accessor-eigenschap met de naam prop te definiëren:

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);

Output:

042

U kunt ook een alleen-lezen eigenschap maken door een getter zonder setter te definiëren:

var obj = { get prop() { return -1; },};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Output:

-1-1

Een eigenschap kan dan worden vervangen door een paar methoden die in het geval de implementatie van een klasse verandert, daarop kunnen reageren, en het gedrag van de klasse kunnen aanpassen. In het ergste geval kan het een exception oproepen om de gebruiker te vertellen dat de eigenschap is afgeschreven en niet meer moet worden gebruikt.

Verdere lectuur

Ik hoop dat je in dit bericht iets nieuws hebt geleerd over het ontwerpen van complexe evolutieve toepassingen in JavaScript. In deze sectie geef ik je een aantal links naar links met meer informatie.

Inleidende artikelen

In deze sectie vind je een aantal korte artikelen waarin de concepten worden gepresenteerd en een overzicht wordt gegeven van de gerelateerde API’s:

  1. Property getters en setters.
  2. JavaScript Getters and Setters

Meer gevorderde artikelen

Deze artikelen gaan over meer gevorderde gerelateerde onderwerpen zoals de problemen die getters en setters met zich meebrengen, en hoe deze op te lossen.

  1. Waarom getters/setters een slecht idee zijn in JavaScript
  2. Data encapsulation in JavaScript

Referentiedocumentatie

Ten slotte, om het onderwerp echt onder de knie te krijgen, de links naar de mozilla-documentatie van de gerelateerde API’s:

  1. get en set maken het mogelijk om getters en setters te definiëren. Verder dan wat we hier hebben uitgelegd. Er zijn andere interessante functies, zoals de ondersteuning voor dynamisch gegenereerde property-namen, en het gebruik van getters en setters om het instellen/verwerven van waarden in een array te simuleren
  2. seal en freeze maken het mogelijk om te bepalen welke eigenschappen van een object al dan niet kunnen worden gewijzigd en hoe. Het kan gebruikt worden om te vermijden dat clients verouderde delen van de API van een object gebruiken.
  3. defineProperty laat toe om getters en setters te definiëren en tegelijk meer controle te hebben over hoe deze eigenschappen door clients gezien worden en hoe wijzigbaar ze zijn.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.