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

handler.setPrototypeOf()

Methode

Trap für die interne Methode <code>[[SetPrototypeOf]]</code>. Sie fängt Aufrufe wie <code>Object.setPrototypeOf()</code> und <code>Reflect.setPrototypeOf()</code> auf einem Proxy ab.

seit JavaScript ES2015 (Baseline: alle modernen Browser, Node.js 6 Kategorie: method-property

Signatur

new Proxy(target, { setPrototypeOf(target, prototype) { … } })

Beschreibung

Die Methode setPrototypeOf() im Handler-Objekt eines Proxy wird immer dann aufgerufen, wenn jemand den Prototyp des Proxys ändern will. Damit kannst du Prototyp-Änderungen protokollieren, prüfen, nur bestimmte Prototypen zulassen oder sie ganz verbieten.

Die Trap fängt folgende Operationen ab:

  • Object.setPrototypeOf(proxy, proto)
  • Reflect.setPrototypeOf(proxy, proto)
  • den Setter proxy.__proto__ = proto (Object.prototype.__proto__)

Die Trap muss einen Wahrheitswert zurückgeben: true heißt, die Änderung war erfolgreich, false heißt, sie ist fehlgeschlagen. Gibt die Trap false zurück, wirft Object.setPrototypeOf() einen TypeError. Reflect.setPrototypeOf() gibt dagegen einfach false zurück. So kannst du dasselbe Verbot sowohl mit Fehler als auch ohne Fehler auswerten.

Wenn du das Standardverhalten beibehalten und nur ergänzen willst, rufst du in der Trap Reflect.setPrototypeOf(target, prototype) auf und gibst dessen Ergebnis zurück.

Parameter

Name Typ Default Beschreibung
$target Pflicht object Das Zielobjekt, das der Proxy umhüllt.
$prototype Pflicht object | null Der neue Prototyp, der gesetzt werden soll, oder null.

Rückgabewert

Typ
boolean
Beschreibung
true, wenn der Prototyp erfolgreich geändert wurde, sonst false. Andere Werte werden in einen Wahrheitswert umgewandelt.

Beispiele

Prototyp-Änderungen protokollieren und weiterreichen

const ziel = {};
const proxy = new Proxy(ziel, {
  setPrototypeOf(target, proto) {
    console.log('Neuer Prototyp:', proto === Array.prototype ? 'Array.prototype' : proto);
    return Reflect.setPrototypeOf(target, proto);
  }
});

Object.setPrototypeOf(proxy, Array.prototype);
console.log(Object.getPrototypeOf(ziel) === Array.prototype);
Neuer Prototyp: Array.prototype true

Prototyp-Änderung verbieten – mit und ohne Fehler

const gesperrt = new Proxy({}, {
  setPrototypeOf() {
    return false; // Änderung ablehnen
  }
});

// Reflect: kein Fehler, nur false
console.log(Reflect.setPrototypeOf(gesperrt, null));

// Object.setPrototypeOf: wirft TypeError
try {
  Object.setPrototypeOf(gesperrt, null);
} catch (err) {
  console.log(err instanceof TypeError);
}
false true

Nur bestimmte Prototypen zulassen

class Basis {}
class Erweitert extends Basis {}

const modell = new Proxy(new Basis(), {
  setPrototypeOf(target, proto) {
    // Nur Prototypen erlauben, die von Basis abstammen
    if (proto !== null && Basis.prototype.isPrototypeOf(proto) || proto === Basis.prototype) {
      return Reflect.setPrototypeOf(target, proto);
    }
    return false;
  }
});

console.log(Reflect.setPrototypeOf(modell, Erweitert.prototype));
console.log(Reflect.setPrototypeOf(modell, Date.prototype));
console.log(modell instanceof Erweitert);
true false true

// Wichtig · Fallstricke

Invariante: Ist das Zielobjekt nicht erweiterbar (Object.preventExtensions(), Object.seal(), Object.freeze()), darf die Trap nur dann true zurückgeben, wenn prototype genau dem aktuellen Prototyp des Ziels entspricht (Object.getPrototypeOf(target)). Gibt sie trotzdem true zurück, wirft die Engine einen TypeError.

Rückgabewert: Gibst du true zurück, ohne den Prototyp wirklich zu ändern, meldet die Operation einen Erfolg, der gar nicht stattgefunden hat. Das führt zu schwer auffindbaren Fehlern. Am sichersten ist es, das Ergebnis von Reflect.setPrototypeOf() zurückzugeben.

Leistung: Engines optimieren Objekte stark anhand ihres Prototyps. Wenn du den Prototyp eines Objekts nachträglich änderst, verlieren viele dieser Optimierungen ihre Wirkung, egal ob mit oder ohne Proxy. Verwende Object.create(), wenn möglich.

Was die Trap nicht abfängt: Object.create() und die class … extends-Syntax setzen den Prototyp direkt beim Erzeugen. Sie lösen diese Trap nicht aus. Den Prototyp auslesen, etwa mit Object.getPrototypeOf(), fängt die Trap getPrototypeOf ab.

Kompatibilität: Wird von allen modernen Browsern und Node.js unterstützt. In Internet Explorer gibt es diese Trap nicht, und sie lässt sich auch nicht nachrüsten (Polyfill).