constructor

Baseline 已广泛支持

此特性已经非常成熟,并且适用于许多设备和浏览器版本。自 2016 年 3 月以来,它已在所有浏览器中可用。

constructor 方法是 的一个特殊方法,用于创建和初始化该类的对象实例。

注意:本页面介绍 constructor 语法。有关所有对象上存在的 constructor 属性,请参阅 Object.prototype.constructor

试一试

class Polygon {
  constructor() {
    this.name = "Polygon";
  }
}

const poly = new Polygon();

console.log(poly.name);
// Expected output: "Polygon"

语法

js
constructor() { /* … */ }
constructor(argument0) { /* … */ }
constructor(argument0, argument1) { /* … */ }
constructor(argument0, argument1, /* …, */ argumentN) { /* … */ }

还有一些额外的语法限制

描述

构造函数使你能够提供任何自定义初始化,这些初始化必须在实例化对象上调用任何其他方法之前完成。

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

如果你没有提供自己的构造函数,那么会为你提供一个默认构造函数。如果你的类是一个基类,默认构造函数是空的。

js
constructor() {}

如果你的类是一个派生类,默认构造函数会调用父构造函数,并传递提供的任何参数。

js
constructor(...args) {
  super(...args);
}

注意:上述显式构造函数和默认构造函数之间的区别在于,后者实际上不会通过 参数展开 调用 数组迭代器

这使得以下代码可以工作:

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;
  }
}

ValidationError 类不需要显式构造函数,因为它不需要进行任何自定义初始化。默认构造函数会负责根据给定的参数初始化父 Error

但是,如果你提供了自己的构造函数,并且你的类派生自某个父类,那么你必须使用 super() 显式调用父类构造函数。例如:

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;
  }
}

对类使用 new 会经历以下步骤:

  1. (如果是派生类)在调用 super() 之前,会评估 constructor 主体。这部分不应访问 this,因为它尚未初始化。
  2. (如果是派生类)评估 super() 调用,它会通过相同的过程初始化父类。
  3. 初始化当前类的 字段
  4. 在调用 super() 之后(如果是基类,则是整个主体)评估 constructor 主体。

constructor 主体中,你可以通过 this 访问正在创建的对象,并通过 new.target 访问使用 new 调用的类。请注意,在执行 constructor 之前,方法(包括 gettersetter)和 原型链 已经在 this 上初始化,因此你甚至可以从超类的构造函数中访问子类的方法。但是,如果这些方法使用 this,则 this 可能尚未完全初始化。这意味着读取派生类的公共字段将导致 undefined,而读取私有字段将导致 TypeError

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
  }
})();

constructor 方法可以有返回值。虽然基类可以从其构造函数返回任何值,但派生类必须返回一个对象或 undefined,否则会抛出 TypeError

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

如果父类构造函数返回一个对象,该对象将被用作 this 值,派生类的 类字段 将在该对象上定义。这种技巧被称为“返回覆盖”,它允许派生类的字段(包括私有字段)在不相关的对象上定义。

constructor 遵循正常的方法语法,因此可以使用参数默认值剩余参数等。

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

构造函数必须是字面名称。计算属性不能成为构造函数。

js
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 的成员都必须是一个普通方法。

示例

使用构造函数

此代码片段取自 类示例实时演示)。

js
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() 调用哪个构造函数。

js
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® 2026 语言规范
# sec-static-semantics-constructormethod

浏览器兼容性

另见