Meta-Programmierung
Die Objekte Proxy
und Reflect
ermöglichen es Ihnen, grundlegende Sprachoperationen (z. B. Eigenschaftsabfrage, Zuweisung, Aufzählung, Funktionsaufruf usw.) abzufangen und benutzerdefiniertes Verhalten zu definieren. Mit der Hilfe dieser beiden Objekte können Sie auf der Metaebene von JavaScript programmieren.
Proxies
Proxy
-Objekte ermöglichen es Ihnen, bestimmte Operationen abzufangen und benutzerdefinierte Verhaltensweisen zu implementieren.
Zum Beispiel das Abrufen einer Eigenschaft eines Objekts:
const handler = {
get(target, name) {
return name in target ? target[name] : 42;
},
};
const p = new Proxy({}, handler);
p.a = 1;
console.log(p.a, p.b); // 1, 42
Das Proxy
-Objekt definiert ein target
(hier ein leeres Objekt) und ein handler
-Objekt, in dem eine get
-Falle implementiert ist. Hier wird ein objekt, das durch einen Proxy gekapselt ist, nicht undefined
zurückgeben, wenn undefinierte Eigenschaften abgefragt werden, sondern stattdessen die Zahl 42
.
Weitere Beispiele finden Sie auf der Proxy
-Referenzseite.
Terminologie
Die folgenden Begriffe werden verwendet, wenn über die Funktionalität von Proxies gesprochen wird.
- handler
-
Platzhalterobjekt, das Fallen enthält.
- traps
-
Die Methoden, die Zugriff auf Eigenschaften bereitstellen. (Dies ist analog zum Konzept der Fallen in Betriebssystemen.)
- target
-
Objekt, das durch den Proxy virtualisiert wird. Es wird häufig als Speicherrückendeckung für den Proxy verwendet. Invarianten (Semantiken, die unverändert bleiben) bezüglich der Nichterweiterbarkeit oder nicht konfigurierbaren Eigenschaften von Objekten werden gegen das Zielobjekt überprüft.
- Invarianten
-
Semantiken, die unverändert bleiben, wenn benutzerdefinierte Operationen implementiert werden, werden Invarianten genannt. Wenn Sie gegen die Invarianten eines Handlers verstoßen, wird ein
TypeError
ausgelöst.
Handler und Fallen
Die folgende Tabelle fasst die verfügbaren Fallen für Proxy
-Objekte zusammen. Siehe die Referenzseiten für detaillierte Erklärungen und Beispiele.
Widerrufbarer Proxy
Die Methode Proxy.revocable()
wird verwendet, um ein widerrufbares Proxy
-Objekt zu erstellen. Dies bedeutet, dass der Proxy über die Funktion revoke
widerrufen und abgeschaltet werden kann.
Danach führt jede Operation am Proxy zu einem TypeError
.
const revocable = Proxy.revocable(
{},
{
get(target, name) {
return `[[${name}]]`;
},
},
);
const proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
revocable.revoke();
console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked
proxy.foo = 1; // TypeError: Cannot perform 'set' on a proxy that has been revoked
delete proxy.foo; // TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked
console.log(typeof proxy); // "object", typeof doesn't trigger any trap
Reflexion
Reflect
ist ein eingebautes Objekt, das Methoden für abfangbare JavaScript-Operationen bereitstellt. Die Methoden sind die gleichen wie die des Proxy-Handlers.
Reflect
ist kein Funktionsobjekt.
Reflect
hilft dabei, Standardoperationen vom Handler an das target
weiterzuleiten.
Mit Reflect.has()
zum Beispiel erhalten Sie den in
-Operator als Funktion:
Reflect.has(Object, "assign"); // true
Eine bessere apply()-Funktion
Vor Reflect
wurde typischerweise die Methode Function.prototype.apply()
verwendet, um eine Funktion mit einem gegebenen this
-Wert und arguments
, bereitgestellt als Array (oder als array-ähnliches Objekt), aufzurufen.
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
Mit Reflect.apply
wird dies weniger umständlich und leichter zu verstehen:
Reflect.apply(Math.floor, undefined, [1.75]);
// 1
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
// "hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;
// 4
Reflect.apply("".charAt, "ponies", [3]);
// "i"
Überprüfung, ob die Eigenschaftsdefinition erfolgreich war
Mit Object.defineProperty
, das ein Objekt zurückgibt, wenn es erfolgreich ist, oder ansonsten einen TypeError
auslöst, würde man einen try...catch
-Block verwenden, um Fehler abzufangen, die bei der Definition einer Eigenschaft auftreten. Da Reflect.defineProperty()
einen Booleschen Erfolgsstatus zurückgibt, können Sie hier einfach einen if...else
-Block verwenden:
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}