Eine kurze Einführung in die Metaprogrammierung in JavaScript
Reflexion
Reflexion, im Gegensatz zur „Codegenerierung“, ist ein Prozess zur Änderung der zugrunde liegenden Mechanik der Sprache. Die Reflexion kann zur Kompilierungszeit oder zur Laufzeit erfolgen, aber wir bleiben bei der Laufzeitreflexion, da wir über JavaScript sprechen, sodass keine Reflexion zur Kompilierungszeit möglich ist. Die hier diskutierten Konzepte könnten jedoch auch auf eine kompilierte Sprache anwendbar sein.
Da wir verstanden haben, dass es bei der Reflexion darum geht, die zugrunde liegende Mechanik der Sprache zu verändern, wurde sie in drei Hauptkategorien unterteilt, nämlich. introspektion, Fürbitte und Modifikation.
Introspektion
Introspektion ist der Prozess der Analyse des Programms. Wenn Sie sagen können, was Programm tut, können Sie es nach Ihren Wünschen ändern. Obwohl einige Programmiersprachen keine Funktionen zur Codegenerierung oder Codeänderung unterstützen, ermöglichen sie höchstwahrscheinlich eine Introspektion.
Ein einfaches Beispiel für Introspektion wäre die Verwendung von typeof
oder instanceof
Operatoren in JavaScript. Die typeof
gibt den aktuellen Datentyp eines Wertes (oder eines Ausdrucks, der einen Wert zurückgibt) zurück, während instanceof
true
oder false
zurückgibt, wenn der LHS-Wert eine Instanz der RHS-Klasse ist. Lassen Sie uns sie in Aktion sehen.
Im obigen Programm haben wir die Operatoren typeof
und instanceof
in der Funktion coerce
verwendet, um den Datentyp eingehender value
. Dies ist die grundlegende Demonstration der Introspektion. Eine Sprache, die speziell für die Metaprogrammierung entwickelt wurde, kann jedoch einige leistungsstarke Introspektionswerkzeuge bereitstellen.
Sie können den Operator in
verwenden, um zu überprüfen, ob eine Eigenschaft im Objekt vorhanden ist. Die globale Funktion isNaN
prüft, ob das Objekt NaN
. Es gibt einige statische Methoden, die um den Object
herum aufgebaut sind, um Werte des Object
-Typs zu überprüfen, z. B. Object.isFrozen(value)
um zu überprüfen, ob value
eingefroren ist oder Object.keys(value)
die Eigenschaftsnamen des value
-Objekts.
Bis ES5 hatten wir diese Operatoren und diese Methoden zum Arbeiten. In ES2015 (ES6) hat JavaScript ein Reflect
Objekt eingeführt, das einige statische Methoden bereitstellt (genau wie Object
), aber speziell für die Selbstbeobachtung entwickelt wurde. Da wir eine separate Lektion zu Reflect
haben, werden diese Methoden dort diskutiert.
Fürbitte
Fürbitte ist der Prozess des Eingreifens in die JavaScript-Prozesse und des Änderns des Standardverhaltens des Prozesses. JavaScript bietet großartige Tools für die Fürbitte, von denen eines Proxy
.
Die Proxy
-Klasse wurde in ES2015 (ES6) eingeführt, um grundlegende JavaScript-Operationen um Objekte abzufangen (zu intervenieren), genau wie wir es oben gesehen haben, aber auf eine viel schönere Weise. Wir haben eine separate Lektion über Proxy
(in Kürze), aber kurz gesagt, Proxy
umschließt eine abfangbare Logik um ein Objekt.
var targetWithProxy = new Proxy(target, handler);
Hier ist das target
Objekt und handler
ist der Interceptor. Das handler
ist ebenfalls ein einfaches JavaScript-Objekt, jedoch mit einigen aussagekräftigen Feldern. For example, handler.get
would be a function that returns a custom value when target.prop
(here, prop
is any property) is accessed.
Proxy is a great way to provide abstractions over your not-so-public data. Im obigen Programm haben wir beispielsweise Abstraktionen über das target
-Objekt bereitgestellt und angepasst, wie es sich der Öffentlichkeit präsentieren soll.
Einige Fürbitten waren auch in ES5 möglich, z. B. die Verwendung von getter
und setters
für Eigenschaftsdeskriptoren, aber dies würde zur Mutation des target
Objekts führen. Proxy
bietet eine viel sauberere Möglichkeit, Fürbitte zu leisten, ohne das ursprüngliche Objekt ändern zu müssen (target
).
Modifikation
Modifikation bezieht sich auf die Modifikation des Programmverhaltens durch Mutation. Im Falle der Intercession haben wir nur die Standard-JavaScript-Prozesse abgefangen, indem wir eine Intercepting-Logik zwischen dem Ziel und dem Empfänger hinzugefügt haben, ohne das Ziel zu beschädigen. In diesem Fall Modifikation, ändern wir das Verhalten des Ziels selbst so passen den Empfänger.
Das Überschreiben einer Funktionsimplementierung wäre ein gutes Beispiel für eine Modifikation. Wenn sich eine Funktion beispielsweise so verhält, dass sie sich auf eine bestimmte Weise verhält, wir aber etwas anderes bedingt möchten, können wir dies tun, indem wir eine selbstübergeordnete Funktion entwerfen. Sehen wir uns ein Beispiel an.