Signatur
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
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);
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));
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);
// 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.