Fange die grundlegenden Operationen der Sprache mit Proxy-Traps ab und delegiere das Standardverhalten sauber mit Reflect.
Diese Lektion in Kodokon öffnenEin Proxy umhüllt ein Zielobjekt und fängt die internen Operationen der Sprache über dreizehn Traps ab: get, set, has (der in-Operator), deleteProperty, ownKeys, apply, construct und so weiter. Das Reflect-Objekt stellt genau dieselben Operationen als Funktionen bereit, mit denselben Signaturen wie die Traps. Es ist die Grundlage der Reaktivität von Vue 3, von dynamischen Mocks und von APIs im ORM-Stil.
const target = { name: "Ada" };
const traced = new Proxy(target, {
get(obj, prop, receiver) {
console.log("get " + String(prop));
return Reflect.get(obj, prop, receiver);
}
});
traced.name; // get nameWarum Reflect.get(obj, prop, receiver) statt obj[prop]? Wegen des Receivers. Ist die Eigenschaft ein Accessor (ein Getter), ruft Reflect.get ihn mit this auf, das auf den Receiver zeigt - den Proxy oder ein davon erbendes Objekt -, während obj[prop] this auf das rohe Ziel setzen würde. Ohne den Receiver brechen Getter, die this lesen, sobald ein Objekt vom Proxy erbt.
const base = {
get label() {
return this.name;
}
};
const proxy = new Proxy(base, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
}
});
const child = Object.create(proxy);
child.name = "Ada";
console.log(child.label); // "Ada" thanks to the receiverDie ernsthaften Anwendungsfälle: Validierung beim Schreiben, virtuelle Eigenschaften, die spontan berechnet werden, Logging, Zugriffskontrolle (das Verbergen von Schlüsseln mit _-Präfix über has und ownKeys) oder Objekte mit Standardverhalten. Beachte, dass Reflect.set einen Boolean zurückgibt, statt im Strict Mode zu werfen: Deine Trap muss diesen Rückgabewert weiterreichen.
function validated(target) {
return new Proxy(target, {
set(obj, prop, value, receiver) {
if (prop === "age" && !Number.isInteger(value)) {
throw new TypeError("age must be an integer");
}
return Reflect.set(obj, prop, value, receiver);
}
});
}
const user = validated({ age: 30 });
user.age = 31; // ok
// user.age = "old"; // TypeErrorget-Trap Reflect.get(target, prop, receiver) gegenüber target[prop] bevorzugen?this (dem Receiver) laufen statt mit dem rohen Zieltarget[prop] die Trap erneut auslösen und eine Endlosrekursion verursachen würde"id" in obj ab?get-Trap gibt 42 für eine Ziel-Eigenschaft zurück, deren Wert 1 ist und die als nicht-konfigurierbar und nicht-schreibbar deklariert ist. Was passiert?