Encapsulation in JavaScript

dit zal mijn laatste bericht zijn in de “JavaScript for (C/)AL Developers” serie vandaag. Als ik bleef bloggen over bijna pure JavaScript spullen, je zou redelijkerwijs kunnen vragen of dit in feite een NAV blog of een JavaScript een. Het is nog steeds NAV, en terwijl de dingen die ik op het punt om te schrijven over is puur een JavaScript-concept, Ik vind het zeer relevant voor elke controle add-in Ontwikkelaar. Dus, houd mijn bier vast, en verdraag me voor een andere.

een van de klachten die ik vaak hoor over JavaScript is dat er in JavaScript geen inkapseling is. Dit is bijna helemaal waar, behalve het feit dat het helemaal niet waar is.

Waar is het probleem in de eerste plaats, en wat is dan de oplossing? Laten we erin duiken.

het probleem

stel je voor dat je een constructor declareert. Stel je voor dat we in de ES5-wereld zijn (die we helaas moeten gebruiken als we willen dat onze control-invoegtoepassingen volledig compatibel zijn met alle platforms waarop zowel NAV als Business Central worden ondersteund).

dit is mijn code:

Dit is duidelijk 42, wat mijn huidige leeftijd is.

binnenkort zal het mijn verjaardag zijn, dus dit zou laten zien 43:

nu, terwijl ik absoluut graag in staat zou zijn om iets als dit te doen in het echte leven:

… dat gaat niet gebeuren. In een fatsoenlijke objectgeoriënteerde wereld, zou “leeftijd” een ingekapselde eigenschap moeten zijn, en je zou niet in staat moeten zijn om Vjeko te bellen.Leeftijd = 25. C# en” normale ” objectgeoriënteerde talen hebben een concept van inkapseling, ze kunnen dit aan met behulp van privé velden, maar JavaScript heeft geen concept van privé. Alles wat hierin gedefinieerd is binnen de object constructor (of later een instantie van een geconstrueerd object) is volledig toegankelijk voor alle code die toegang heeft tot die instantie. In mijn voorbeeld hierboven kan iedereen leeftijd instellen en ermee wegkomen.

je zou kunnen zeggen dat JavaScript geen inkapseling heeft. En zoals ik al zei, Je zou het helemaal mis hebben.

de oplossing

hoe duidelijk het ook is dat we niets direct als privé kunnen verklaren, er zijn nog steeds dingen die we kunnen gebruiken. Een mooi concept dat van pas komt heet sluitingen. Sluitingen worden uitgebreid uitgelegd op een miljoen blogs, documentatie sites, en code voorbeelden over het hele internet, en u kunt google de bejesus uit hen in uw eigen tempo, dus Ik zal niet verdiepen in het uitleggen van sluitingen hier. Ik ga ze gewoon toepassen om het inkapselingsprobleem op te lossen.

er zijn minstens twee manieren om dit aan te pakken. Laten we eerst de meer voor de hand liggende doen: een access functie.

stel je de wereld voor zonder eigenschappen waar je geen object kunt doen.property = waarde (zoals vjeko.Leeftijd = 25 in ons geval, zoveel als ik zou helemaal houden van het!). In die wereld zou je getter en setter functies hebben:

(negeer even het feit dat ik dit nog steeds gebruik.leeftijd om “encapsulate” de “property”)

uiteraard kun je deze als volgt noemen:

dan, als je leeftijd als alleen-lezen wilt hebben, zou je gewoon de setAge setter functie laten vallen. Als dit.leeftijd was echt privé (wat het niet is), dit zou de truc voor u doen. Het probleem is, niets wat hierop gedefinieerd is is privé, het is toegankelijk voor iedereen die toegang heeft tot een instantie van het object. Het is zo openbaar als maar kan.

om dit probleem op te lossen, is het eerste wat we moeten doen de getter functie declaratie van het prototype naar de instantie te verplaatsen. Prototypeleden zijn het dichtst bij wat we statisch zouden noemen in C#, ook al hebben ze in runtime bepaalde gedragskenmerken van zowel static als instance leden. Echter, laten we eerst het lid uit de buurt van het prototype, en op instantie:

dit Lost alleen het eerste deel van het probleem op, het feit dat getAge werd gedefinieerd op het prototype in plaats van op de instantie. Echter, met een eenvoudige verandering als deze:

… we lossen het probleem volledig op:

de leeftijd is nu volledig ingekapseld. Je kunt het openen via de getter-functie, maar je kunt het niet direct instellen omdat het alleen toegankelijk is in het afsluitingsgebied van de dit.getAge instantie functie.

om de Persoonsklasse volledig te implementeren, moet u de growol-functie van het prototype naar de instantie verplaatsen:

en dit werkt precies zoals je wilt dat het werkt:

maar waarom werkt het? Het werkt als gevolg van sluitingen. De leeftijd parameter van de constructor werd gevangen in de sluiting scope van zowel getAge en growOlder functies, zodat u toegang tot de waarde van beide functies, maar waardoor het volledig ontoegankelijk voor iemand anders, ergens anders.

nog betere oplossing

je zou kunnen zeggen dat je het niet wilt benaderen via een getter functie, en dat je volledige read-only property syntaxis nodig hebt. U wilt uw leeftijd alleen lezen en ingekapseld op hetzelfde moment. Hebbes! JavaScript kan dat niet samenvatten! Behalve dat het absoluut kan.

ik richtte het Object aan.defineProperty methode in mijn vorige post, en als je dat leest, kunt u onmiddellijk zien hoe het hier kan worden toegepast.

dus, laten we een alleen-lezen instantie eigenschap van persoon klasse definiëren:

daar. Het deed geen pijn. En het werkt:

dus daar heb je het. Full-blown inkapseling in JavaScript om u te helpen goede, geïsoleerde code te schrijven, en neem uw controle add-ins naar echte kick-A$$ niveau.

Gelukkig JavaScript, en ik hoop meer tijd te krijgen om te bloggen over andere coole en nuttige JavaScript tips en trucs voor (C/)al ontwikkelaars.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.