Signatur
Beschreibung
new.target ist eine Meta-Eigenschaft. Sie ist also kein normales Objekt mit Eigenschaft, sondern eine besondere Syntaxform, die in jeder Funktion und in jedem Klassenkörper zur Verfügung steht. Wird eine Funktion mit new aufgerufen, verweist new.target auf den Konstruktor, den new direkt aufgerufen hat. Bei einem normalen Funktionsaufruf ist der Wert undefined.
Typische Einsatzfälle sind:
- Aufruf ohne
newerkennen: In klassischen Konstruktorfunktionen kann man damit eine Fehlermeldung werfen oder den Aufruf automatisch aufnewumleiten. - Abstrakte Basisklassen: Prüft man
new.target === Basisklasse, lässt sich verhindern, dass die Basisklasse direkt instanziiert wird. Nur Unterklassen sind dann erlaubt. - Fabrik- und Registrierungslogik: Im Konstruktor der Basisklasse verweist
new.targetauf die tatsächlich instanziierte Unterklasse. So kommt man etwa annew.target.nameoder an statische Eigenschaften der Unterklasse.
In Klassen-Konstruktoren ist new.target nie undefined, weil Klassen ohnehin nur mit new aufgerufen werden können. Pfeilfunktionen haben kein eigenes new.target. Sie übernehmen den Wert aus der umgebenden Funktion, genau wie bei this. Außerhalb von Funktionen, also direkt im Skript oder Modul, führt new.target zu einem SyntaxError.
Rückgabewert
new direkt angewendet wurde. Bei Vererbung ist das die abgeleitete Klasse. Ohne new ist der Wert undefined. Bei Reflect.construct(target, args, newTarget) ist es der übergebene newTarget.Beispiele
Aufruf ohne new erkennen und abfangen
function User(name) {
if (!new.target) {
// Ohne new aufgerufen: automatisch korrigieren
return new User(name);
}
this.name = name;
}
const a = new User('Anna');
const b = User('Ben'); // kein new
console.log(a instanceof User, a.name);
console.log(b instanceof User, b.name);
Abstrakte Basisklasse erzwingen
class Shape {
constructor() {
if (new.target === Shape) {
throw new TypeError('Shape ist abstrakt und kann nicht direkt instanziiert werden.');
}
}
area() {
throw new Error('area() muss überschrieben werden');
}
}
class Circle extends Shape {
constructor(r) {
super();
this.r = r;
}
area() {
return Math.PI * this.r ** 2;
}
}
console.log(new Circle(2).area().toFixed(2));
try {
new Shape();
} catch (err) {
console.log(err.message);
}
Unterklasse im Basiskonstruktor erkennen
class Model {
static tableName = 'models';
constructor() {
// new.target zeigt auf die tatsächlich instanziierte Klasse
this.type = new.target.name;
this.table = new.target.tableName;
}
}
class Product extends Model {
static tableName = 'products';
}
console.log(new Product());
console.log(new Model());
Pfeilfunktionen und Reflect.construct
function Outer() {
const inner = () => new.target; // übernimmt new.target von Outer
return { fromArrow: inner() === Outer };
}
console.log(new Outer().fromArrow);
function Base() {
this.target = new.target.name;
}
class Other {}
const obj = Reflect.construct(Base, [], Other);
console.log(obj.target, obj instanceof Other);
// Wichtig · Fallstricke
- Nur innerhalb von Funktionen erlaubt: Auf oberster Ebene eines Skripts oder Moduls führt
new.targetzu einemSyntaxError. Innerhalb voneval()ist es nur zulässig, wenn der Aufruf in einer Funktion steht. - Pfeilfunktionen haben kein eigenes
new.target. Sie lassen sich ohnehin nicht mitnewaufrufen. Deshalb sehen sie den Wert der umgebenden Funktion. In Klassenfeld-Initialisierern und statischen Blöcken istnew.targetimmerundefined. - Methoden-Aufrufe: Normale Methoden wie
obj.method()und Funktionen, die übercall/applyaufgerufen werden, habennew.target === undefined. - Die Prüfung
this instanceof Fooist kein gleichwertiger Ersatz. Sie lässt sich mitFoo.call(Object.create(Foo.prototype))austricksen,new.targetdagegen nicht. new.targetverweist bei Vererbung immer auf die äußerste Klasse. Wer die Basisklasse vergleichen will, sollte deshalb ausdrücklichnew.target === Baseprüfen.- Kompatibilität: Wird seit ES2015 von allen modernen Browsern und Node.js unterstützt, nicht vom Internet Explorer. Transpiler wie Babel können es nur teilweise nachbilden.