Articles

een korte inleiding tot Metaprogrammering in JavaScript

reflectie

reflectie, niet-gelaakte “codegeneratie”, is een proces om de onderliggende mechanica van de taal te veranderen. Reflectie kan gebeuren tijdens het compileren of tijdens de runtime, maar we zullen vasthouden aan runtime reflectie als we het hebben over JavaScript, dus compilatietijd reflectie zal niet mogelijk zijn. De hier besproken Concepten kunnen echter ook van toepassing zijn op een gecompileerde taal.

aangezien we begrepen hebben dat de reflectie alles draait om het veranderen van de onderliggende mechanica van de taal, is deze verdeeld in drie hoofdcategorieën, te weten: introspectie, voorspraak en wijziging.

introspectie

introspectie is het proces van het analyseren van het programma. Als u kunt vertellen wat programma doet, kunt u het wijzigen als per uw likings. Hoewel sommige programmeertalen geen codegeneratie of codewijzigingsfuncties ondersteunen, staan ze hoogstwaarschijnlijk introspectie toe.

een eenvoudig voorbeeld van introspectie is het gebruik van typeof of instanceof operators in JavaScript. De typeof geeft het huidige gegevenstype van een waarde (of een uitdrukking die een waarde retourneert) terug, terwijl instanceof geeft true of false als de LHS-waarde een instantie van de RHS-klasse is. Laten we ze in actie zien.

(inleiding/introspectie.js)

in het bovenstaande programma hebben we typeof en instanceof operators gebruikt in de coerce functie om het gegevenstype van inkomende . Dit is de basis demonstratie van introspectie. Echter, een taal die specifiek is ontworpen voor metaprogrammering kan een aantal krachtige introspectie tools bieden.

u kunt de operator in gebruiken om te controleren of er een eigenschap in het object bestaat. De Globale functie isNaN controleert of het object NaNis. Er zijn enkele statische methoden gebouwd rond het Object om waarden van het Object te inspecteren, zoals Object.isFrozen(value) om te controleren of value bevroren is of Object.keys(value) om de eigenschapsnamen van het value object.

tot ES5 hadden we deze operators en deze methoden om mee te werken. In ES2015 (ES6) introduceerde JavaScript Reflect object dat een aantal statische methoden biedt (net als Object) maar specifiek ontworpen voor introspectie. Omdat we een aparte les hebben over Reflect, worden deze methoden hier besproken.

Intercessie

Intercessie is het proces van ingrijpen in de JavaScript-processen en het aanpassen van het standaardgedrag van het proces. JavaScript biedt geweldige tools voor voorbede, waaronder Proxy.

De Proxy klasse werd geà ntroduceerd in ES2015 (ES6) om basis JavaScript operaties rond objecten te onderscheppen (tussen te komen), net zoals we hierboven hebben gezien, maar op een veel mooiere manier. We hebben een aparte les over Proxy (binnenkort beschikbaar) maar in een notendop, Proxy wikkelt een onderscheppende logica rond een object.

var targetWithProxy = new Proxy(target, handler);

Hier is de target object en handler de interceptor. De handler is ook een gewoon JavaScript-object, maar met enkele betekenisvolle velden. For example, handler.get would be a function that returns a custom value when target.prop (here, prop is any property) is accessed.

(introduction/proxy.js)

Proxy is a great way to provide abstractions over your not-so-public data. In het bovenstaande programma hebben we bijvoorbeeld abstracties gegeven over target object en hebben we aangepast hoe het zichzelf aan het publiek moet presenteren.

enige voorspraak was mogelijk in ES5, zoals het gebruik van getter en setters op property descriptors, maar het zou resulteren in de mutatie van het target object. Proxy biedt een veel schonere manier om voorbede te bereiken zonder het oorspronkelijke object te hoeven wijzigen (target).

modificatie

modificatie verwijst naar de modificatie van het gedrag van het programma door middel van mutatie. In het geval van voorspraak onderschepten we alleen de standaard JavaScript-processen door een onderscheppende logica toe te voegen tussen het doel en de ontvanger zonder het doel te schaden. In dit geval wijzigen we het gedrag van het doel zelf zodat het past bij de ontvanger.

het overschrijven van een functieimplementatie zou een goed voorbeeld van wijziging zijn. Bijvoorbeeld, als een functie is ontworpen om zich op een bepaalde manier te gedragen, maar we willen iets anders voorwaardelijk, kunnen we dat doen door een zelfoverheersende functie te ontwerpen. Laat een voorbeeld zien.

(introduction/function-modification.js)

in het bovenstaande voorbeeld hebben we een functie gemaakt die zichzelf overschrijft met een nieuwe functie-implementatie. Dit zou het hardste voorbeeld van modificatie zijn, maar we hebben andere, misschien zinvollere use cases.

(introduction/readonly-object.js)

in het bovenstaande voorbeeld hebben we Object.defineProperty() methode gebruikt om de standaard eigenschap descriptor van de name eigenschap te wijzigen om het alleen-lezen te maken. U kunt ook deObject.freeze() methode gebruiken om het gehele object te vergrendelen om mutaties te voorkomen.

sommige voorbeden kunnen plaatsvinden door middel van wijzigingen zoals u kunt uit het bovenstaande voorbeeld. Door het instellen van writable:false in de eigenschap descriptor van het object, dus muteren van het object (interne implementatie), hebben we de waarde toewijzing operatie.

Als u niet bekend bent met de valueOf methode, wordt deze gebruikt om een object te dwingen tot een primitieve waarde. Dus als ik een object heb en het heeft valueOf methode op zichzelf of op zijn prototype keten, dan wordt deze methode aangeroepen door JavaScript wanneer je probeert een rekenkundige bewerking op het uit te voeren. Standaard heeft Object de methode valueOf die zichzelf (het object) retourneert.

(introduction/value of.js)

zoals u in het bovenstaande voorbeeld kunt zien, resulteerde emp1/10 in een NaN omdat een object niet kan worden verdeeld als natuurlijke getallen. Maar later hebben we valueOf methode toegevoegd aan Employee klasse die salary waarde van het object retourneert. Daarom gaf emp2/10200 omdat emp2.salary is 200. Evenzo gaf emp3/10300 zoals we valueOf methode direct op de emp3hebben toegevoegd.

dus bij elke stap in het bovenstaande voorbeeld interveniëren we hoe een object wordt gepresenteerd aan een standaard JavaScript-operatie en veranderen we zijn gedrag naar onze voorkeuren. Dit is niets anders dan voorspraak.

in ES2015 (ES6) heeft JavaScript een nieuw primitief gegevenstype geïntroduceerd dat symbolis. Het is niets zoals we eerder hebben gezien en kan niet worden weergegeven in letterlijke vorm. Het kan alleen geconstrueerd worden door de functie Symbol aan te roepen.

var sym1 = Symbol();
var sym2 = Symbol();
var sym3 = Symbol('description'); // description for debugging aidsym1 === sym2 // false
sym1 === sym2 // falsetypeof sym1 // 'symbol'console.log( sym1 ); // 'Symbol()'
console.log( sym3 ); // 'Symbol(description)'

In een notendop produceert het unieke waarden die ook kunnen worden gebruikt als reguliere objecttoetsen met behulp van de obj notatie waarbij key een symbool zou zijn.

var key = Symbol();var obj = {
name: 'Ross',
: 200
};console.log( obj.name ); // 'Ross'
console.log( obj ); // 200
Object.keys(obj); // obj = 300;

omdat ze uniek zijn, is er geen manier om per ongeluk een duplicaat te maken. Elk nieuw symbool is uniek (aangemaakt met Symbol()) wat betekent dat als u hetzelfde symbool wilt gebruiken, u dat in een variabele moet opslaan en die variabele moet doorgeven om naar hetzelfde symbool te verwijzen.

in het voorbeeld valueOf kunt u het probleem herkennen als we niet voorzichtig of bewust zijn. Aangezien valueOf eenstring eigenschap is (zoals in emp3), kan iedereen het per ongeluk overschrijven of iemand die niet op de hoogte is van valueOf zou het voor eigen gebruik kunnen gebruiken denkend ” wat zit er in de naam?”.

omdat symbolen ook als objectsleutels kunnen worden gebruikt, heeft JavaScript een aantal globale symbolen verstrekt die gebruikt zouden moeten worden als objectsleutels voor sommige standaard JavaScript-bewerkingen. Omdat deze symbolen bekend zijn bij een ontwikkelaar, worden ze “bekende symbolen”genoemd. Deze bekende symbolen worden aan het publiek blootgesteld als de statische eigenschappen van de functie Symbol.

een van de bekende symbolen is Symbol.toPrimitive die gebruikt moet worden als de sleutel van het object om zijn primitieve waarde te verkrijgen. Ja, je denkt goed, het is een vervanging van valueOf methode en het heeft de voorkeur.

(introduction/symbol-toPrimitive.js)

💡 de methode toPrimitive doet meer dan alleen een getalwaarde van het object retourneren. Lees de symbolen lessen om er meer over te weten.

JavaScript biedt veel van dergelijke bekende symbolen om het standaard JavaScript-gedrag rond objecten te onderscheppen en te wijzigen. We zullen hierover praten en symbolen in het algemeen in de symbolen les.