构造函数
**constructor
** 方法是 类 的一个特殊方法,用于创建和初始化该类的对象实例。
**注意:**此页面介绍了 constructor
语法。有关所有对象上存在的 constructor
属性,请参见 Object.prototype.constructor
。
试一试
语法
描述
构造函数使您能够提供在对实例化对象调用任何其他方法之前必须完成的任何自定义初始化。
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
如果您不提供自己的构造函数,则会为您提供一个默认构造函数。如果您的类是基类,则默认构造函数为空
constructor() {}
如果您的类是派生类,则默认构造函数会调用父构造函数,并将传递给它的任何参数一起传递
constructor(...args) {
super(...args);
}
这使得像这样的代码能够正常工作
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;
}
}
ValidationError
类不需要显式构造函数,因为它不需要进行任何自定义初始化。然后,默认构造函数负责从给定的参数初始化父 Error
。
但是,如果您提供了自己的构造函数,并且您的类从某个父类派生,那么您必须使用 super()
显式调用父类构造函数。例如
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;
}
}
使用 new
对类进行调用会经历以下步骤
- (如果它是派生类)在
super()
调用之前的constructor
主体将被评估。此部分不应该访问this
,因为它尚未初始化。 - (如果它是派生类)将评估
super()
调用,这将通过相同的过程初始化父类。 - 当前类的 字段 将被初始化。
- 将评估
super()
调用之后的constructor
主体(或者如果是基类,则评估整个主体)。
在 constructor
主体中,您可以通过 this
访问正在创建的对象,并通过 new
访问被调用的类,方法是使用 new.target
。请注意,方法(包括 getter 和 setter)以及 原型链 在执行 constructor
之前已经在 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
// 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
}
})();
constructor
方法可能有一个返回值。虽然基类可以从其构造函数返回任何东西,但派生类必须返回一个对象或 undefined
,否则会抛出 TypeError
。
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
如果父类构造函数返回一个对象,则该对象将用作将在其上定义派生类的 类字段 的 this
值。这个技巧被称为 "返回覆盖",它允许派生类的字段(包括 私有 字段)在不相关的对象上定义。
constructor
遵循正常的 方法 语法,因此可以全部使用 参数默认值、rest 参数 等。
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
构造函数必须是一个文字名称。计算属性 不能成为构造函数。
class Foo {
// This is a computed property. It will not be picked up as a constructor.
["constructor"]() {
console.log("called");
this.a = 1;
}
}
const foo = new Foo(); // No log
console.log(foo); // Foo {}
foo.constructor(); // Logs "called"
console.log(foo); // Foo { a: 1 }
异步方法、生成器方法、访问器和类字段禁止被调用为 constructor
。私有名称不能被称为 #constructor
。任何名为 constructor
的成员必须是一个普通方法。
示例
使用构造函数
class Square extends Polygon {
constructor(length) {
// Here, it calls the parent class' constructor with lengths
// provided for the Polygon's width and height
super(length, length);
// NOTE: In derived classes, `super()` must be called before you
// can use `this`. Leaving this out will cause a ReferenceError.
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();
}
}
// Make Square extend Rectangle (which is a base class) instead of Polygon
Object.setPrototypeOf(Square, Rectangle);
const newInstance = new Square();
// newInstance is still an instance of Polygon, because we didn't
// change the prototype of Square.prototype, so the prototype chain
// of newInstance is still
// newInstance --> Square.prototype --> Polygon.prototype
console.log(newInstance instanceof Polygon); // true
console.log(newInstance instanceof Rectangle); // false
// However, because super() calls Rectangle as constructor, the name property
// of newInstance is initialized with the logic in Rectangle
console.log(newInstance.name); // Rectangle
规范
规范 |
---|
ECMAScript 语言规范 # sec-static-semantics-constructormethod |
浏览器兼容性
BCD 表仅在启用 JavaScript 的浏览器中加载。