Start · Sprachen · JavaScript · Referenz · Reflect.construct()

Reflect.construct()

Methode

Ruft einen Konstruktor wie mit dem <code>new</code>-Operator auf, nur als Funktion: Die Argumente werden als Array übergeben, <code>new.target</code> lässt sich optional festlegen.

seit JavaScript ES2015 Kategorie: method-property

Signatur

Reflect.construct(target, argumentsList, newTarget)

Beschreibung

Reflect.construct() ist die funktionale Form von new target(...argumentsList). Die Argumente werden als array-ähnliches Objekt übergeben. So kann man Konstruktoren dynamisch aufrufen, ohne die Anzahl der Argumente vorher zu kennen. Das ist besonders in Fabrik-Funktionen, Dependency-Injection-Containern und Proxy-Traps nützlich.

Ein Vorteil gegenüber new ist der optionale dritte Parameter newTarget. Er legt fest, welchen Wert new.target im Konstruktor hat und von welchem prototype das neue Objekt erbt. Der Code von target läuft also, das Ergebnis ist aber eine Instanz von newTarget. Mit Object.create() und Function.prototype.apply() lässt sich das nicht korrekt nachbauen, weil dort new.target undefined bleibt und eingebaute Klassen wie Date oder Map ihre internen Slots nicht bekommen.

Typischer Einsatz ist der construct-Trap eines Proxy. Dort gibt Reflect.construct(target, args, newTarget) den Aufruf unverändert an das Originalziel weiter, und Vererbung sowie new.target bleiben dabei erhalten.

Parameter

Name Typ Default Beschreibung
$target Pflicht Function Der Konstruktor, der aufgerufen wird. Er muss konstruierbar sein, sonst wird ein TypeError geworfen. Pfeilfunktionen und normale Methoden sind zum Beispiel nicht konstruierbar.
$argumentsList Pflicht Array|ArrayLike Array-ähnliches Objekt mit den Argumenten für target. Es ist Pflicht; ohne Argumente übergibt man [].
$newTarget Function target Der Konstruktor, dessen prototype das neue Objekt bekommt. Im Konstruktor ist er als new.target sichtbar. Auch er muss konstruierbar sein.

Beispiele

Grundlegender Aufruf statt new

const d1 = new Date(2024, 0, 15);
const d2 = Reflect.construct(Date, [2024, 0, 15]);

console.log(d1.getTime() === d2.getTime());
console.log(d2 instanceof Date);
true true

Dynamische Fabrik-Funktion

class User {
  constructor(name, role = 'gast') {
    this.name = name;
    this.role = role;
  }
}
class Product {
  constructor(title, price) {
    this.title = title;
    this.price = price;
  }
}

const registry = { User, Product };

const create = (typeName, ...args) => {
  const Ctor = registry[typeName];
  if (!Ctor) throw new Error(`Unbekannter Typ: ${typeName}`);
  return Reflect.construct(Ctor, args);
};

console.log(create('User', 'Anna', 'admin'));
console.log(create('Product', 'Tastatur', 49.9));
User { name: 'Anna', role: 'admin' } Product { title: 'Tastatur', price: 49.9 }

newTarget: Konstruktor-Code von A, Prototyp von B

class Animal {
  constructor(name) {
    this.name = name;
    console.log('new.target:', new.target.name);
  }
}
class Dog {
  bark() { return `${this.name} bellt!`; }
}

const rex = Reflect.construct(Animal, ['Rex'], Dog);
console.log(rex instanceof Dog, rex instanceof Animal);
console.log(rex.bark());
new.target: Dog true false Rex bellt!

Proxy-construct-Trap mit Protokollierung

class Connection {
  constructor(host, port) {
    this.url = `${host}:${port}`;
  }
}

const LoggedConnection = new Proxy(Connection, {
  construct(target, args, newTarget) {
    console.log(`Erzeuge ${target.name} mit`, args);
    // new.target und Prototyp bleiben erhalten
    return Reflect.construct(target, args, newTarget);
  }
});

const conn = new LoggedConnection('localhost', 5432);
console.log(conn.url, conn instanceof Connection);
Erzeuge Connection mit [ 'localhost', 5432 ] localhost:5432 true

// Wichtig · Fallstricke

  • TypeError bei nicht konstruierbaren Zielen: Pfeilfunktionen, Methoden aus Kurzschreibweise, async-Funktionen, Generatoren und Funktionen wie Math.max lösen einen TypeError aus. Dasselbe gilt, wenn newTarget nicht konstruierbar ist.
  • argumentsList ist Pflicht: Reflect.construct(Foo) ohne zweites Argument wirft einen TypeError. Ohne Argumente übergibt man [].
  • Anderes Verhalten als Object.create() + apply(): Bei diesem älteren Muster ist new.target undefined. Klassen lassen sich damit gar nicht aufrufen, weil ein Klassenkonstruktor ohne new einen TypeError wirft. Eingebaute Objekte wie Date, Map oder Error bekommen außerdem ihre internen Slots nicht. Reflect.construct() hat diese Probleme nicht.
  • Proxy-Traps: Im construct-Trap immer newTarget weiterreichen. Sonst funktioniert class Sub extends ProxiedClass nicht richtig, weil die Instanz den falschen Prototyp bekommt.
  • Kompatibilität: Wird von allen modernen Browsern und von Node.js ab Version 6 unterstützt. Der Internet Explorer kennt die Methode nicht.