Articles

En kort introduktion til metaprogrammering i JavaScript

refleksion

refleksion, i modsætning til “kodegenerering”, er en proces til at ændre sprogets underliggende mekanik. Refleksion kan ske ved kompileringstid eller ved kørsel, men vi holder os til runtime reflection, da vi taler om JavaScript, så kompileringstidsreflektion vil ikke være mulig. Imidlertid kan begreber, der diskuteres her, også gælde for et kompileret sprog.

da vi har forstået, at refleksionen handler om at ændre sprogets underliggende mekanik, er den opdelt i tre hovedkategorier, nemlig. introspektion, forbøn og modifikation.

introspektion

introspektion er processen med at analysere programmet. Hvis du kan fortælle, hvad programmet gør, kan du ændre det som pr dine likings. Selvom nogle programmeringssprog ikke understøtter kodegenerering eller kodemodifikationsfunktioner, men de tillader sandsynligvis introspektion.

et simpelt eksempel på introspektion ville brugetypeofellerinstanceof operatører i JavaScript. typeof returnerer den aktuelle datatype for en værdi (eller et udtryk, der returnerer en værdi), mens instanceof returnerer true eller false hvis LHS-værdi er en forekomst af RHS-klasse. Lad os se dem i aktion.

(introduktion / introspektion.js)

i ovenstående program har vi brugt typeof og instanceof operatører i coerce funktion til at snuse datatypen for indgående value. Dette er den grundlæggende demonstration af introspektion. Et sprog, der er specielt designet til metaprogrammering, kan dog give nogle kraftfulde introspektionsværktøjer.

Du kan brugein operatoren til at kontrollere, om der findes en egenskab i objektet. isNaNglobal funktion kontrollerer, om objektet erNaN. Der er nogle statiske metoder bygget op omkring Object for at inspicere værdier af Object typen som Object.isFrozen(value) for at kontrollere, om value er frosset eller Object.keys(value) for at få egenskabsnavnene på value objektet.

indtil ES5 havde vi disse operatører og disse metoder til at arbejde med. I ES2015 (ES6) introducerede JavaScript Reflect objekt, der giver nogle statiske metoder (ligesom Object), men specielt designet til introspektion. Da vi har en separat lektion om Reflect, diskuteres disse metoder der.

Forbøn

Forbøn er processen med at gribe ind i JavaScript-processerne og ændre procesens standardadfærd. JavaScript giver gode værktøjer til forbøn hvoraf den ene er Proxy.

Proxy klassen blev introduceret i ES2015 (ES6) for at opfange (gribe ind) grundlæggende JavaScript-operationer omkring objekter, ligesom vi har set ovenfor, men på en meget pænere måde. Vi har en separat lektion om Proxy(kommer snart), men i en nøddeskal, Proxy vikler en interceptabel logik omkring et objekt.

var targetWithProxy = new Proxy(target, handler);

Her er target Er objekt og handler er interceptor. handler er også et almindeligt JavaScript-objekt, men med nogle meningsfulde felter. 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. For eksempel har vi i ovenstående program leveret abstraktioner over target objekt og tilpasset, hvordan det skal præsentere sig for offentligheden.

nogle forbøn var også mulige i ES5, såsom at bruge getterog setterspå ejendomsdeskriptorer, men det ville resultere i mutationen af target objekt. Proxygiver en meget renere måde at opnå forbøn på uden at skulle ændre det originale objekt (target).

modifikation

modifikation refererer til modifikationen af programadfærden gennem mutation. I tilfælde af forbøn opfangede vi kun standard JavaScript-processerne ved at tilføje en aflytningslogik mellem målet og modtageren uden at skade målet. I dette tilfælde ændring, er vi ændre opførslen af målet selv så passer modtageren.

tilsidesættelse af en funktionsimplementering ville være et godt eksempel på ændring. For eksempel, hvis en funktion er designet til at opføre sig på en bestemt måde, men vi ønsker noget andet betinget, kan vi gøre det ved at designe en selvoverstyrende funktion. Lad os se et eksempel.

(introduktion / funktion-modifikation.js)

i ovenstående eksempel har vi oprettet en funktion, der tilsidesætter sig selv med en ny funktionsimplementering. Dette ville være det hårdeste eksempel på ændring, men vi har andre, måske mere meningsfulde brugssager.

(introduktion / readonly-object.js)

i ovenstående eksempel har vi brugt Object.defineProperty() metode til at ændre standardegenskabsbeskrivelsen for name egenskaben for at gøre den skrivebeskyttet. Du kan også bruge Object.freeze() – metoden til at låse hele objektet for at undgå mutationer.

nogle forbøn kan ske gennem ændringer, som du kan fra ovenstående eksempel. Ved at indstille writable:false i objektets egenskabsbeskrivelse, derfor muterer objektet (intern implementering), har vi påbegyndt værditildelingsoperationen.

Hvis du ikke er bekendt medvalueOf – metoden, bruges den til at tvinge et objekt til en primitiv værdi. Så hvis jeg har et objekt, og det har valueOf metode på sig selv eller på sin prototype kæde, så kaldes denne metode af JavaScript, når du forsøger at udføre en aritmetisk operation på den. Som standard Objecthar valueOf metode, der returnerer sig selv (objektet).

(introduktion/valueof.js)

som du kan se i ovenstående eksempel,emp1/10 resulterede i enNaN da et objekt ikke kan opdeles som naturlige tal. Men senere har vi tilføjet valueOf metode på Employee klasse, der returnerer salary værdien af objektet. Derforemp2/10 returnerede200 sidenemp2.salary er200. Tilsvarendeemp3/10 returnerede300 som vi har tilføjetvalueOf metode direkte påemp3.

så ved hvert trin i ovenstående eksempel griber vi ind, hvordan et objekt præsenteres for en standard JavaScript-operation og ændrer dets adfærd til vores smag. Dette er intet andet end forbøn.

i ES2015 (ES6) har JavaScript introduceret en ny primitiv datatype, som ersymbol. Det er intet som vi har set før og kan ikke repræsenteres i bogstavelig form. Det kan kun konstrueres ved at kalde Symbol – funktionen.

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

i en nøddeskal producerer den unikke værdier, der også kan bruges som almindelige objekttaster ved hjælp afobjnotation hvorkey ville være et symbol.

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

da de er unikke, er der ingen måde at oprette et duplikatsymbol ved et uheld. Hvert nyt symbol er unikt (oprettet ved hjælp af Symbol()), hvilket betyder, at hvis du vil bruge et samme symbol, skal du gemme det i en variabel og videregive den variabel rundt for at henvise til det samme symbol.

i eksempletvalueOf kan du se problemet, hvis vi ikke er forsigtige eller Opmærksomme. Siden valueOf er en string ejendom (som i emp3) , kan enhver tilsidesætte det ved et uheld eller nogen, der ikke ved om valueOf kunne planlægge at bruge det til eget brug og tænke “Hvad er det, der er i navnet?”.

da symboler også kan bruges som objektnøgler, har JavaScript leveret nogle globale symboler, der skal bruges som objektnøgler til nogle standard JavaScript-operationer. Da disse symboler er velkendte for en udvikler, kaldes de”velkendte symboler”. Disse velkendte symboler udsættes for offentligheden som de statiske egenskaber ved Symbol funktion.

et af de velkendte symboler er Symbol.toPrimitive som skal bruges som objektets nøgle for at opnå sin primitive værdi. Ja, du tænker rigtigt, det er en erstatning af valueOf metode, og det er foretrukket.

(introduktion / symbol-toprimitiv.js)

rettoPrimitive metode gør mere end blot at returnere en talværdi af objektet. Læs symbolerne lektioner for at vide mere om det.

JavaScript giver mange sådanne velkendte symboler til at opfange og ændre standard JavaScript-adfærd omkring objekter. Vi vil tale om dette og symboler generelt i Symbolundervisningen.