Start · Sprachen · JavaScript · Referenz · handler.defineProperty()

handler.defineProperty()

Methode

Trap eines <code>Proxy</code>-Handlers für die interne Methode <code>[[DefineOwnProperty]]</code>. Sie fängt z. B. <code>Object.defineProperty()</code> und <code>Reflect.defineProperty()</code> ab.

seit JavaScript ES2015 Kategorie: method-property

Signatur

handler.defineProperty(target, property, descriptor)

Beschreibung

Die Methode defineProperty() im Handler-Objekt eines Proxy wird immer dann aufgerufen, wenn auf dem Proxy eine Eigenschaft definiert oder umdefiniert werden soll. Das passiert bei Object.defineProperty(), Object.defineProperties() und Reflect.defineProperty(). Auch eine normale Zuweisung wie proxy.x = 1 landet hier, wenn keine set-Trap vorhanden ist. Der Grund: Das Standardverhalten von [[Set]] ruft am Ende [[DefineOwnProperty]] auf dem Empfänger auf.

Typische Einsatzzwecke:

  • Validieren von Eigenschaftsnamen oder Deskriptoren, z. B. Accessor-Eigenschaften verbieten
  • Schützen privater oder reservierter Schlüssel, etwa solcher mit Präfix _
  • Protokollieren und Beobachten von Strukturänderungen an Objekten

Die Trap muss einen Wahrheitswert zurückgeben, der angibt, ob die Definition geklappt hat. Bei false wirft Object.defineProperty() einen TypeError, während Reflect.defineProperty() einfach false zurückgibt. Eine Zuweisung im Strict Mode wirft ebenfalls einen TypeError. Um das Standardverhalten weiterzuleiten, ruft man am einfachsten Reflect.defineProperty(target, property, descriptor) auf.

Parameter

Name Typ Default Beschreibung
$target Pflicht object Das Zielobjekt, das der Proxy umhüllt.
$property Pflicht string | symbol Name oder Symbol der Eigenschaft, deren Deskriptor definiert werden soll.
$descriptor Pflicht object Der normalisierte Eigenschafts-Deskriptor. Er enthält nur Standardfelder (value, writable, get, set, enumerable, configurable), und nur die, die tatsächlich angegeben wurden.
$this object handler Innerhalb der Trap zeigt this auf das Handler-Objekt.

Rückgabewert

Typ
boolean
Beschreibung
true, wenn die Eigenschaft erfolgreich definiert wurde, sonst false. Andere Rückgabewerte werden in einen Wahrheitswert umgewandelt.

Beispiele

Definitionen protokollieren und weiterleiten

const target = {};
const proxy = new Proxy(target, {
  defineProperty(target, prop, descriptor) {
    console.log(`Definiere "${String(prop)}":`, descriptor);
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

Object.defineProperty(proxy, 'id', { value: 42, enumerable: true });
console.log(target.id);
Definiere "id": { value: 42, enumerable: true } 42

Private Schlüssel mit Unterstrich schützen

'use strict';
const user = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    if (typeof prop === 'string' && prop.startsWith('_')) {
      return false; // Definition verweigern
    }
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

user.name = 'Anna'; // ohne set-Trap läuft das über defineProperty
console.log(Reflect.defineProperty(user, '_secret', { value: 1 }));

try {
  user._token = 'abc';
} catch (err) {
  console.log(err.constructor.name);
}
console.log(Object.keys(user));
false TypeError [ 'name' ]

Accessor-Eigenschaften verbieten (nur Datenwerte erlauben)

const config = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    if ('get' in descriptor || 'set' in descriptor) {
      throw new TypeError(`Getter/Setter für "${String(prop)}" nicht erlaubt`);
    }
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

config.port = 8080;
try {
  Object.defineProperty(config, 'host', { get: () => 'localhost' });
} catch (err) {
  console.log(err.message);
}
console.log(config.port);
Getter/Setter für "host" nicht erlaubt 8080

// Wichtig · Fallstricke

Invarianten: Verletzt die Trap eine dieser Regeln, wirft die Engine einen TypeError, auch wenn die Trap true zurückgibt:

  • Ist das Ziel nicht erweiterbar (Object.isExtensible(target) === false), darf keine neue Eigenschaft als hinzugefügt gemeldet werden.
  • Eine Eigenschaft darf nur dann als nicht konfigurierbar gemeldet werden, wenn sie auf dem Ziel tatsächlich als eigene, nicht konfigurierbare Eigenschaft existiert.
  • Eine nicht konfigurierbare Eigenschaft darf nur dann als nicht beschreibbar gemeldet werden, wenn sie auf dem Ziel ebenfalls nicht beschreibbar ist.
  • Ist auf dem Ziel eine passende Eigenschaft vorhanden, muss der Deskriptor mit ihr kompatibel sein (vgl. Object.defineProperty()).

Zusammenspiel mit set: Gibt es eine set-Trap, die nicht über Reflect.set(..., receiver) weiterleitet, wird defineProperty bei Zuweisungen nicht mehr ausgelöst. Leitet set mit dem Proxy als receiver weiter, feuern beide Traps.

Nur Standardfelder: Eigene Zusatzfelder im Deskriptor (z. B. { value: 1, foo: true }) werden vor dem Aufruf der Trap entfernt.

Rückgabewert: Vergisst man das return, liefert die Trap undefined, also false. Im Strict Mode führt das zu unerwarteten TypeErrors. Unterstützt wird die Trap in allen modernen Browsern und in Node.js ab Version 6. Proxies lassen sich nicht polyfillen.