Mit einem Konstruktor können Sie eine benutzerdefinierte Initialisierung vornehmen, die erforderlich ist, bevor andere Methoden auf einem instanziierten Objekt aufgerufen werden können.
js
class Person {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`Hello, my name is ${this.name}`);
}
}
const otto = new Person("Otto");
otto.introduce(); // Hello, my name is Otto
Wenn Sie keinen eigenen Konstruktor bereitstellen, wird ein Standardkonstruktor verwendet.
Bei einer Basisklasse ist der Standardkonstruktor leer:
Bei einer abgeleiteten Klasse ruft der Standardkonstruktor den Konstruktor der Elternklasse auf und gibt alle übergebenen Argumente weiter:
js
constructor(...args) {
super(...args);
}
Hinweis:
Der Unterschied zwischen einem expliziten Konstruktor wie dem oben gezeigten und dem Standardkonstruktor besteht darin, dass Letzterer bei der Verteilung der Argumente nicht tatsächlich den Array-Iterator aufruft.
Dadurch funktioniert beispielsweise folgender Code:
js
class ValidationError extends Error {
printCustomerMessage() {
return `Validation failed :-( (details: ${this.message})`;
}
}
try {
throw new ValidationError("Not a valid phone number");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // This is Error instead of ValidationError!
console.log(error.printCustomerMessage());
} else {
console.log("Unknown error", error);
throw error;
}
}
Die Klasse ValidationError benötigt keinen expliziten Konstruktor, da sie keine benutzerdefinierte Initialisierung vornehmen muss.
Der Standardkonstruktor übernimmt die Initialisierung der Elternklasse Error mit dem übergebenen Argument.
Wenn Sie jedoch einen eigenen Konstruktor bereitstellen und Ihre Klasse von einer Elternklasse abgeleitet ist, müssen Sie den Konstruktor der Elternklasse ausdrücklich mit super() aufrufen.
Zum Beispiel:
js
class ValidationError extends Error {
constructor(message) {
super(message); // call parent class constructor
this.name = "ValidationError";
this.code = "42";
}
printCustomerMessage() {
return `Validation failed :-( (details: ${this.message}, code: ${this.code})`;
}
}
try {
throw new ValidationError("Not a valid phone number");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // Now this is ValidationError!
console.log(error.printCustomerMessage());
} else {
console.log("Unknown error", error);
throw error;
}
}
Bei der Verwendung von new mit einer Klasse werden die folgenden Schritte durchlaufen:
- Bei einer abgeleiteten Klasse wird der Teil des
constructor-Rumpfs vor dem Aufruf von super() ausgeführt. Dieser Teil darf nicht auf this zugreifen, da es noch nicht initialisiert ist.
- Bei einer abgeleiteten Klasse wird
super() aufgerufen, wodurch die Elternklasse nach demselben Verfahren initialisiert wird.
- Die Felder der aktuellen Klasse werden initialisiert.
- Der Teil des
constructor-Rumpfs nach dem Aufruf von super() wird ausgeführt. Bei einer Basisklasse wird der gesamte Rumpf ausgeführt.
Innerhalb des constructor-Rumpfs können Sie über this auf das zu erstellende Objekt und über new.target auf die Klasse zugreifen, mit der new aufgerufen wurde. Beachten Sie, dass Methoden (einschließlich Getter und Setter) und die Prototypkette auf this bereits initialisiert sind, bevor constructor ausgeführt wird. Daher können Sie vom Konstruktor der Oberklasse aus sogar auf Methoden der Unterklasse zugreifen. Wenn diese Methoden jedoch this verwenden, ist this zu diesem Zeitpunkt noch nicht vollständig initialisiert. Das bedeutet, dass das Lesen öffentlicher Felder der abgeleiteten Klasse undefined ergibt, während das Lesen privater Felder einen TypeError auslöst.
js
new (class C extends class B {
constructor() {
console.log(this.foo());
}
} {
#a = 1;
foo() {
return this.#a; // TypeError: Cannot read private member #a from an object whose class did not declare it
// It's not really because the class didn't declare it,
// but because the private field isn't initialized yet
// when the superclass constructor is running
}
})();
Die constructor-Methode kann einen Wert zurückgeben. Während der Konstruktor einer Basisklasse einen beliebigen Wert zurückgeben kann, muss der Konstruktor einer abgeleiteten Klasse ein Objekt oder undefined zurückgeben. Andernfalls wird ein TypeError ausgelöst.
js
class ParentClass {
constructor() {
return 1;
}
}
console.log(new ParentClass()); // ParentClass {}
// The return value is ignored because it's not an object
// This is consistent with function constructors
class ChildClass extends ParentClass {
constructor() {
return 1;
}
}
console.log(new ChildClass()); // TypeError: Derived constructors may only return object or undefined
Wenn der Konstruktor der Elternklasse ein Objekt zurückgibt, wird dieses Objekt als this-Wert verwendet, auf dem die Klassenfelder der abgeleiteten Klasse definiert werden. Dieser Mechanismus wird als „return overriding“ bezeichnet. Er ermöglicht es, Felder einer abgeleiteten Klasse (auch private Felder) auf Objekten zu definieren, die nicht zur Klassenhierarchie gehören.
Für constructor gilt die normale Methodensyntax. Daher können unter anderem Standardwerte für Parameter und Rest-Parameter verwendet werden.
js
class Person {
constructor(name = "Anonymous") {
this.name = name;
}
introduce() {
console.log(`Hello, my name is ${this.name}`);
}
}
const person = new Person();
person.introduce(); // Hello, my name is Anonymous
Der Name des Konstruktors muss wörtlich angegeben werden. Berechnete Eigenschaften können keine Konstruktoren sein.
Nicht statische asynchrone Methoden, Generatormethoden und Accessors dürfen nicht den nicht berechneten Namen constructor tragen. Auch statische und Instanz-Klassenfelder dürfen nicht den nicht berechneten Namen constructor tragen. Private Namen dürfen nicht #constructor lauten. Jedes nicht statische Mitglied mit dem nicht berechneten Namen constructor muss eine gewöhnliche Methode sein.