Start · Sprachen · JavaScript · Referenz · new Proxy()

new Proxy()

Methode

Der Konstruktor <code>Proxy()</code> erzeugt ein Proxy-Objekt, das grundlegende Operationen wie Lesen, Schreiben oder Aufrufen auf einem Zielobjekt abfangen und umdefinieren kann.

seit JavaScript ES2015 Kategorie: method-property

Signatur

new Proxy(target, handler)

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

Typ
Proxy
Beschreibung
Ein neues Proxy-Objekt. Für den Aufrufer verhält es sich wie 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);
dark nicht gesetzt

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);
}
42 Ungültiges Alter: -5

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;
count: 0 -> 1 count: 1 -> 10 count: undefined -> undefined

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));
slowSum(1, 2, 3) dauerte 0.012 ms 6

// Wichtig · Fallstricke

  • Nur mit new: Proxy(target, handler) ohne new wirft einen TypeError. Sind target oder handler keine Objekte, wird ebenfalls ein TypeError geworfen.
  • Invarianten: Traps müssen bestimmte Regeln einhalten. Eine get-Trap darf etwa für eine nicht beschreibbare, nicht konfigurierbare Eigenschaft keinen anderen Wert liefern. Eine set-Trap, die im Strict Mode false zurückgibt, löst einen TypeError aus.
  • Interne Slots: Eingebaute Objekte wie Map, Set, Date oder 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 der get-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.
  • receiver weitergeben: Reiche in get/set immer den receiver an Reflect weiter, 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().