构造函数
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.
constructor
是一种用于创建和初始化 class
对象实例的特殊方法。
备注:
本页介绍 constructor
语法。关于所有对象的 constructor
属性,请参见 Object.prototype.constructor
。
尝试一下
语法
描述
通过构造函数,你可以在调用实例化对象的其他方法之前,提供必须完成的自定义初始化。
class Person {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`你好,我的名字是 ${this.name}`);
}
}
const otto = new Person("Otto");
otto.introduce(); // 你好,我的名字是 Otto
如果不指定构造函数,则使用默认的构造函数。如果你的类是基类,默认构造函数会是空的:
constructor() {}
如果你的类是派生类,默认构造函数会调用父构造函数,并传递所提供的任何参数:
constructor(...args) {
super(...args);
}
这样代码才能正常工作:
class ValidationError extends Error {
printCustomerMessage() {
return `验证失败 :-((详细信息:${this.message})`;
}
}
try {
throw new ValidationError("非有效电话号码");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // 这是 Error,而不是 ValidationError!
console.log(error.printCustomerMessage());
} else {
console.log("未知错误", error);
throw error;
}
}
ValidationError
类不需要显式构造函数,因为它不需要进行任何自定义初始化。默认构造函数会根据给定的参数初始化父类 Error
。
但是,如果你提供了自己的构造函数,而你的类派生自某个父类,那么你必须使用 super()
显式地调用父类的构造函数。例如:
class ValidationError extends Error {
constructor(message) {
super(message); // 调用父类构造函数
this.name = "ValidationError";
this.code = "42";
}
printCustomerMessage() {
return `发生未知错误 :-((详细信息:${this.message},错误代码:${this.code})`;
}
}
try {
throw new ValidationError("非有效手机号码");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // 现在这是 ValidationError!
console.log(error.printCustomerMessage());
} else {
console.log("未知错误", error);
throw error;
}
}
在类中使用 new
,需要经过以下步骤:
- (如果是派生类)
super()
调用之前的constructor
主体。这部分不应访问this
,因为它尚未初始化。 - (如果是派生类)执行
super()
调用,通过同样的过程初始化父类。 - 当前类的字段将被初始化。
- 执行调用
super()
后的constructor
主体(如果是基类,则对整个主体)。
在 constructor
主体中,你可以通过 this
访问正在创建的对象,并通过 new.target
访问用 new
调用的类。请注意,在执行 constructor
之前,方法(包括 getter 和 setter)和原型链已在 this
上初始化,因此你甚至可以从父类的构造函数访问子类的方法。但是,如果这些方法使用了 this
,这时 this
尚未完全初始化。这意味着读取派生类的公共字段的结果是 undefined
,而读取私有字段将导致 TypeError
。
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
// 这并不是因为 class 没有声明它,
// 而是由于私有字段在父类构造函数运行的时候尚未初始化
}
})();
constructor
方法可能有返回值。基类可以在构造函数中返回任何值,而派生类必须返回一个对象、 undefined
值,或抛出 TypeError
。
class ParentClass {
constructor() {
return 1;
}
}
console.log(new ParentClass()); // ParentClass {}
// 因为返回值不是一个对象,所以它会被忽略
// 这与函数构造函数一致
class ChildClass extends ParentClass {
constructor() {
return 1;
}
}
console.log(new ChildClass()); TypeError: Derived constructors may only return object or undefined
如果父类构造函数返回一个对象,则该对象将被用作 this
值,派生类的类字段将在该值上定义。这种技巧被称为“返回重载”,它允许在无关对象上定义派生类的字段(包括私有字段)。
constructor
遵循正常的方法语法,因此参数默认值、剩余参数等都可以使用。
class Person {
constructor(name = "Anonymous") {
this.name = name;
}
introduce() {
console.log(`你好,我的名字是 ${this.name}`);
}
}
const person = new Person();
person.introduce(); // 你好,我的名字是 Anonymous
构造函数必须是一个明确的值。计算属性不能成为构造函数。
class Foo {
// 这是一个计算属性。它不会作为构造函数被拾取。
["constructor"]() {
console.log("被调用");
this.a = 1;
}
}
const foo = new Foo(); // 无日志
console.log(foo); // Foo {}
foo.constructor(); // 记录“被调用”
console.log(foo); // Foo { a: 1 }
禁止将异步方法、生成器方法、访问器和类字段称为 constructor
。私有名称不能被命名为 #constructor
。任何名为 constructor
的成员都必须是普通方法。
示例
使用constructor
方法
class Square extends Polygon {
constructor(length) {
// 在这里,它调用了父类的构造函数,并将 lengths 提供给 Polygon 的"width"和"height"
super(length, length);
// 注意:在派生类中,必须先调用 super() 才能使用 "this"。
// 忽略这个,将会导致一个引用错误。
this.name = "Square";
}
get area() {
return this.height * this.width;
}
set area(value) {
this.height = value ** 0.5;
this.width = value ** 0.5;
}
}
在绑定了不同原型的构造函数中调用 super
super()
调用当前类原型的构造函数。如果更改了当前类的原型,super()
将调用新原型的构造函数。更改当前类的 prototype
属性的原型不会影响 super()
调用哪个构造函数。
class Polygon {
constructor() {
this.name = "Polygon";
}
}
class Rectangle {
constructor() {
this.name = "Rectangle";
}
}
class Square extends Polygon {
constructor() {
super();
}
}
// 让 Square 扩展 Rectangle(这是一个基类),而不是 Polygon
Object.setPrototypeOf(Square, Rectangle);
const newInstance = new Square();
// newInstance 仍然是 Polygon 的实例,因为我们没有
// 没有改变 Square.prototype 的原型,所以 newInstance 的
// 原型链仍然是
// newInstance --> Square.prototype --> Polygon.prototype
console.log(newInstance instanceof Polygon); // true
console.log(newInstance instanceof Rectangle); // false
// 然而,由于 super() 调用 Rectangle 作为构造函数,
// newInstance 的 name 属性将按照 Rectangle 中的逻辑进行初始化
console.log(newInstance.name); // Rectangle
规范
Specification |
---|
ECMAScript Language Specification # sec-static-semantics-constructormethod |
浏览器兼容性
BCD tables only load in the browser