Signatur
Beschreibung
Mit new Proxy(target, handler) legst du eine „Hülle“ um ein beliebiges Objekt (target). Jede Operation auf dem Proxy, etwa ein Eigenschaftszugriff, eine Zuweisung, in, delete oder ein Funktionsaufruf, wird an eine passende Trap-Funktion im handler-Objekt weitergereicht. Fehlt eine Trap, wird die Operation unverändert an das Zielobjekt weitergegeben.
Typische Einsatzgebiete sind Validierung beim Setzen von Werten, Standardwerte für fehlende Eigenschaften, Protokollierung und Debugging, Reaktivität (z. B. in Vue 3 oder MobX), schreibgeschützte Sichten sowie das Virtualisieren von Objekten, deren Eigenschaften erst beim Zugriff berechnet werden.
Verfügbare Traps sind: get, set, has, deleteProperty, ownKeys, getOwnPropertyDescriptor, defineProperty, getPrototypeOf, setPrototypeOf, isExtensible, preventExtensions, apply (nur bei Funktionszielen) und construct (nur bei Konstruktor-Zielen). Innerhalb der Traps greifst du am besten über die gleichnamigen Reflect-Methoden auf das Standardverhalten zurück.
Proxy lässt sich nur mit new aufrufen. Ein Aufruf ohne new wirft einen TypeError. Proxys haben kein prototype-Objekt, deshalb funktioniert instanceof Proxy nicht.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | object | Function | Das Zielobjekt, das der Proxy umhüllt. Jeder Objekttyp ist erlaubt, auch Arrays, Funktionen und andere Proxys, aber keine Primitive. | |
| $handler Pflicht | object | Objekt mit Trap-Funktionen wie get, set oder apply, die das Verhalten des Proxys festlegen. Ein leeres Objekt {} ergibt einen reinen Weiterleitungs-Proxy. |
Rückgabewert
target, die Operationen laufen aber über die Traps im handler.Beispiele
Standardwerte für fehlende Eigenschaften
const config = { theme: 'dark' };
const withDefaults = new Proxy(config, {
get(target, prop, receiver) {
// Fehlende Schlüssel liefern einen Standardwert statt undefined
return prop in target ? Reflect.get(target, prop, receiver) : 'nicht gesetzt';
}
});
console.log(withDefaults.theme);
console.log(withDefaults.language);
Validierung beim Setzen von Werten
const user = new Proxy({}, {
set(target, prop, value, receiver) {
if (prop === 'age' && (!Number.isInteger(value) || value < 0)) {
throw new TypeError(`Ungültiges Alter: ${value}`);
}
return Reflect.set(target, prop, value, receiver);
}
});
user.age = 42;
console.log(user.age);
try {
user.age = -5;
} catch (err) {
console.log(err.message);
}
Einfache Reaktivität: Änderungen beobachten
const observe = (obj, onChange) => new Proxy(obj, {
set(target, prop, value, receiver) {
const old = target[prop];
const ok = Reflect.set(target, prop, value, receiver);
if (old !== value) onChange(prop, old, value);
return ok;
},
deleteProperty(target, prop) {
const ok = Reflect.deleteProperty(target, prop);
onChange(prop, target[prop], undefined);
return ok;
}
});
const state = observe({ count: 0 }, (key, alt, neu) => {
console.log(`${key}: ${alt} -> ${neu}`);
});
state.count++;
state.count = 10;
delete state.count;
Funktionsaufrufe messen mit der apply-Trap
const slowSum = (...nums) => nums.reduce((a, b) => a + b, 0);
const timed = new Proxy(slowSum, {
apply(target, thisArg, args) {
const start = performance.now();
const result = Reflect.apply(target, thisArg, args);
console.log(`${target.name}(${args.join(', ')}) dauerte ${(performance.now() - start).toFixed(3)} ms`);
return result;
}
});
console.log(timed(1, 2, 3));
// Wichtig · Fallstricke
- Nur mit
new:Proxy(target, handler)ohnenewwirft einenTypeError. Sindtargetoderhandlerkeine Objekte, wird ebenfalls einTypeErrorgeworfen. - Invarianten: Traps müssen bestimmte Regeln einhalten. Eine
get-Trap darf etwa für eine nicht beschreibbare, nicht konfigurierbare Eigenschaft keinen anderen Wert liefern. Eineset-Trap, die im Strict Modefalsezurückgibt, löst einenTypeErroraus. - Interne Slots: Eingebaute Objekte wie
Map,Set,Dateoder private Klassenfelder (#feld) arbeiten mit internen Slots. Methodenaufrufe über den Proxy schlagen dann fehl (TypeError: incompatible receiver). Abhilfe schafft das Binden der Methoden an das Ziel innerhalb derget-Trap. - Identität: Der Proxy ist ein anderes Objekt als das Ziel (
proxy !== target). Änderungen direkt am Ziel umgehen die Traps. - Performance: Jeder Zugriff läuft über eine Funktion. In Hot Paths oder bei sehr großen Datenmengen ist das spürbar langsamer als direkter Zugriff.
receiverweitergeben: Reiche inget/setimmer denreceiveranReflectweiter, damit Getter/Setter und Vererbung korrekt funktionieren.- Kompatibilität: Wird in allen modernen Browsern und seit Node.js 6 unterstützt. Proxys lassen sich nicht polyfillen, im Internet Explorer gibt es sie nicht.
- Für widerrufbare Proxys gibt es
Proxy.revocable().