构造函数
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 的一个特殊方法,用于创建和初始化该类的对象实例。
¥The constructor
method is a special method of a class for creating and initializing an object instance of that class.
注意:本页介绍
constructor
语法。有关所有对象上存在的constructor
属性,请参阅Object.prototype.constructor
。¥Note: This page introduces the
constructor
syntax. For theconstructor
property present on all objects, seeObject.prototype.constructor
.
Try it
语法
¥Syntax
constructor() { /* … */ }
constructor(argument0) { /* … */ }
constructor(argument0, argument1) { /* … */ }
constructor(argument0, argument1, /* …, */ argumentN) { /* … */ }
还有一些额外的语法限制:
¥There are some additional syntax restrictions:
描述
¥Description
构造函数使你能够提供在实例化对象上调用任何其他方法之前必须完成的任何自定义初始化。
¥A constructor enables you to provide any custom initialization that must be done before any other methods can be called on an instantiated object.
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
如果你不提供自己的构造函数,那么将为你提供默认构造函数。如果你的类是基类,则默认构造函数为空:
¥If you don't provide your own constructor, then a default constructor will be supplied for you. If your class is a base class, the default constructor is empty:
constructor() {}
如果你的类是派生类,则默认构造函数将调用父构造函数,并传递提供的任何参数:
¥If your class is a derived class, the default constructor calls the parent constructor, passing along any arguments that were provided:
constructor(...args) {
super(...args);
}
注意:像上面这样的显式构造函数和默认构造函数之间的区别在于,后者实际上并不调用 数组迭代器 到 争论蔓延。
¥Note: The difference between an explicit constructor like the one above and the default constructor is that the latter doesn't actually invoke the array iterator through argument spreading.
这使得这样的代码能够工作:
¥That enables code like this to work:
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
。
¥The ValidationError
class doesn't need an explicit constructor, because it doesn't need to do any custom initialization.
The default constructor then takes care of initializing the parent Error
from the argument it is given.
但是,如果你提供自己的构造函数,并且你的类派生自某个父类,则必须使用 super()
显式调用父类构造函数。例如:
¥However, if you provide your own constructor, and your class derives from some parent class, then you must explicitly call the parent class constructor using super()
.
For example:
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
需要经过以下步骤:
¥Using new
on a class goes through the following steps:
- (如果是派生类)评估
super()
调用之前的constructor
主体。该部分不应访问this
,因为它尚未初始化。 - (如果是派生类)评估
super()
调用,它通过相同的过程初始化父类。 - 当前类的 fields 已初始化。
- 评估
super()
调用后的constructor
主体(或整个主体,如果它是基类)。
在 constructor
主体内,你可以访问通过 this
创建的对象,并访问通过 new
到 new.target
调用的类。请注意,在执行 constructor
之前,方法(包括 getters 和 setters)和 原型链 已经在 this
上初始化,因此你甚至可以从超类的构造函数访问子类的方法。但是,如果这些方法使用 this
,则 this
将尚未完全初始化。这意味着读取派生类的公共字段将导致 undefined
,而读取私有字段将导致 TypeError
。
¥Within the constructor
body, you can access the object being created through this
and access the class that is called with new
through new.target
. Note that methods (including getters and setters) and the prototype chain are already initialized on this
before the constructor
is executed, so you can even access methods of the subclass from the constructor of the superclass. However, if those methods use this
, the this
will not have been fully initialized yet. This means reading public fields of the derived class will result in undefined
, while reading private fields will result in a 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
。
¥The constructor
method may have a return value. While the base class may return anything from its constructor, the derived class must return an object or undefined
, or a TypeError
will be thrown.
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
值。这个技巧称为 "返回覆盖",它允许派生类的字段(包括 private 字段)在不相关的对象上定义。
¥If the parent class constructor returns an object, that object will be used as the this
value on which class fields of the derived class will be defined. This trick is called "return overriding", which allows a derived class's fields (including private ones) to be defined on unrelated objects.
constructor
遵循正常的 method 语法,因此 参数默认值、其余参数 等都可以使用。
¥The constructor
follows normal method syntax, so parameter default values, rest parameters, etc. can all be used.
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
构造函数必须是字面名称。计算属性 不能成为构造函数。
¥The constructor must be a literal name. Computed properties cannot become constructors.
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
的成员都必须是普通方法。
¥Async methods, generator methods, accessors, and class fields are forbidden from being called constructor
. Private names cannot be called #constructor
. Any member named constructor
must be a plain method.
示例
使用构造函数
¥Using the constructor
¥This code snippet is taken from the classes sample (live demo).
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
¥Calling super in a constructor bound to a different prototype
super()
调用当前类原型的构造函数。如果你更改当前类本身的原型,super()
将调用新原型的构造函数。更改当前类的 prototype
属性的原型不会影响 super()
调用哪个构造函数。
¥super()
calls the constructor that's the prototype of the current class. If you change the prototype of the current class itself, super()
will call the constructor that's the new prototype. Changing the prototype of the current class's prototype
property doesn't affect which constructor super()
calls.
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
规范
Specification |
---|
ECMAScript Language Specification # sec-static-semantics-constructormethod |
浏览器兼容性
BCD tables only load in the browser