此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。
Object.prototype.constructor
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2015年7月.
Object 实例的constructor 数据属性返回一个引用,指向创建该实例对象的构造函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
备注:这是 JavaScript 对象的一个属性。关于类的constructor 方法,请参见其参考页面。
In this article
值
对创建该实例对象的构造函数的引用。
Object.prototype.constructor 的属性特性 | |
|---|---|
| 可写 | 是 |
| 可枚举 | 否 |
| 可配置 | 是 |
备注:这个属性默认会在每个构造函数的prototype 属性上创建,并由该构造函数创建的所有对象继承。
描述
除了null 原型对象之外,任何对象都会在其[[Prototype]] 上有一个constructor 属性。使用字面量创建的对象也会有一个指向该对象构造函数类型的constructor 属性,例如,数组字面量创建的Array 对象和对象字面量创建的普通对象。
const o1 = {};o1.constructor === Object; // trueconst o2 = new Object();o2.constructor === Object; // trueconst a1 = [];a1.constructor === Array; // trueconst a2 = new Array();a2.constructor === Array; // trueconst n = 3;n.constructor === Number; // true请注意,constructor 属性通常来自构造函数的prototype 属性。如果你有一个更长的原型链,通常可以假定链中的每个对象都有一个constructor 属性。
const o = new TypeError(); // 继承关系:TypeError -> Error -> Objectconst proto = Object.getPrototypeOf;proto(o).constructor === TypeError; // trueproto(proto(o)).constructor === Error; // trueproto(proto(proto(o))).constructor === Object; // true示例
>打印对象的构造函数
下面这个示例创建一个构造函数(Tree),以及该类型的对象(theTree)。然后打印了theTree 对象的constructor 属性。
function Tree(name) { this.name = name;}const theTree = new Tree("Redwood");console.log(`theTree.constructor 是 ${theTree.constructor}`);这个示例会打印以下输出:
theTree.constructor 是 function Tree(name) { this.name = name;}为对象的 constructor 属性赋值
可以为非基本类型对象的constructor 属性赋值。
const arr = [];arr.constructor = String;arr.constructor === String; // truearr instanceof String; // falsearr instanceof Array; // trueconst foo = new Foo();foo.constructor = "bar";foo.constructor === "bar"; // true// 等等…这不会覆盖旧的constructor 属性——它实际上存在于实例的[[Prototype]] 中,而不是作为其自有属性。
const arr = [];Object.hasOwn(arr, "constructor"); // falseObject.hasOwn(Object.getPrototypeOf(arr), "constructor"); // truearr.constructor = String;Object.hasOwn(arr, "constructor"); // true——实例属性会覆盖原型链上的同名属性但是,即使对Object.getPrototypeOf(a).constructor 重新赋值,它也不会改变对象的其他行为。例如,instanceof 的行为由Symbol.hasInstance 控制,而不是由constructor 控制:
const arr = [];arr.constructor = String;arr instanceof String; // falsearr instanceof Array; // trueconstructor 属性没有受到保护,可以被重新赋值或被覆盖,因此在检测变量类型时,通常应避免使用它,而应该使用更不易出错的方法,如对于对象使用instanceof 和Symbol.toStringTag,对于基本类型使用typeof。
更改构造函数原型对象的 constructor 属性
每个构造函数都有一个prototype 属性,当通过new 运算符调用时,该属性将成为实例的[[Prototype]]。因此,ConstructorFunction.prototype.constructor 将成为实例的[[Prototype]] 上的属性,如前面所述。
然而,如果对ConstructorFunction.prototype 重新赋值,constructor 属性将丢失。例如,以下是创建继承模式的常见方式:
function Parent() { // …}Parent.prototype.parentMethod = function () {};function Child() { Parent.call(this); // 确保所有内容都已正确初始化}// 将 Child.prototype 的 [[Prototype]] 指向 Parent.prototypeChild.prototype = Object.create(Parent.prototype);由于重新赋值了Child.prototype,Child 实例的constructor 将是Parent。
通常情况下,这不是什么大问题——JavaScript 几乎从不读取对象的constructor 属性。唯一的例外是在使用[Symbol.species] 创建类的新实例时,但这种情况很少见,并且你应该使用extends 语法来子类化内置对象。
然而,在某些调用使用constructor 从实例中访问原始类时,确保Child.prototype.constructor 总是指向Child 本身非常重要。考虑这种情况:对象具有create() 方法来创建自身。
function Parent() { // …}function CreatedConstructor() { Parent.call(this);}CreatedConstructor.prototype = Object.create(Parent.prototype);CreatedConstructor.prototype.create = function () { return new this.constructor();};new CreatedConstructor().create().create(); // TypeError: new CreatedConstructor().create().create is undefined,因为 constructor === Parent在上面的示例中,会抛出一个异常,因为constructor 链接到Parent。为了避免这种情况,只需将其赋值为你将要使用的必要构造函数即可。
function Parent() { // …}function CreatedConstructor() { // …}CreatedConstructor.prototype = Object.create(Parent.prototype, { // 将原始构造函数返回给 Child constructor: { value: CreatedConstructor, enumerable: false, // 使其不可枚举,这样它就不会出现在 `for...in` 循环中 writable: true, configurable: true, },});CreatedConstructor.prototype.create = function () { return new this.constructor();};new CreatedConstructor().create().create(); // 跑起来没毛病请注意,当手动添加constructor 属性时,将属性设置为不可枚举非常重要,这将确保constructor 就不会在for...in 循环中被访问——尽管通常情况下不会被访问。
如果上面的代码看起来太死板,你也可以考虑使用Object.setPrototypeOf() 来操作原型链。
function Parent() { // …}function CreatedConstructor() { // …}Object.setPrototypeOf(CreatedConstructor.prototype, Parent.prototype);CreatedConstructor.prototype.create = function () { return new this.constructor();};new CreatedConstructor().create().create(); // 在不重新创建 constructor 属性的情况下仍然有效Object.setPrototypeOf() 存在潜在的性能缺陷,因为所有先前创建的涉及该原型链的对象都必须重新编译;但是,如果上述初始化代码发生在Parent 或CreatedConstructor 构造之前,其影响应该是很小的。
接下来,看另外一个相关示例。
function ParentWithStatic() {}ParentWithStatic.startPosition = { x: 0, y: 0 }; // 静态成员属性ParentWithStatic.getStartPosition = function () { return this.startPosition;};function Child(x, y) { this.position = { x, y };}Child.prototype = Object.create(ParentWithStatic.prototype, { // 将原始构造函数返回给 Child constructor: { value: Child, enumerable: false, writable: true, configurable: true, },});Child.prototype.getOffsetByInitialPosition = function () { const position = this.position; // 使用 `this.constructor`,以期 `getStartPosition` 存在于一个静态方法中。 const startPosition = this.constructor.getStartPosition(); return { offsetX: startPosition.x - position.x, offsetY: startPosition.y - position.y, };};new Child(1, 1).getOffsetByInitialPosition();// Error: this.constructor.getStartPosition is undefined,// 因为构造函数是 Child,它没有 getStartPosition 静态方法如果想要保证示例正常运行,我们需要让Parent 作为构造函数,或给Child 的构造分配静态属性:
// …Object.assign(Child, ParentWithStatic); // 注意,在创建 Child 的原型前我们先分配它的值Child.prototype = Object.create(ParentWithStatic.prototype, { // 将原始构造函数返回给 Child constructor: { value: Child, enumerable: false, writable: true, configurable: true, },});// …但更好的方法是,我们可以使构造函数本身相互继承,就像类的extends 一样。
function ParentWithStatic() {}ParentWithStatic.startPosition = { x: 0, y: 0 }; // 静态成员属性ParentWithStatic.getStartPosition = function () { return this.startPosition;};function Child(x, y) { this.position = { x, y };}// 正确地创建继承关系!Object.setPrototypeOf(Child.prototype, ParentWithStatic.prototype);Object.setPrototypeOf(Child, ParentWithStatic);Child.prototype.getOffsetByInitialPosition = function () { const position = this.position; const startPosition = this.constructor.getStartPosition(); return { offsetX: startPosition.x - position.x, offsetY: startPosition.y - position.y, };};console.log(new Child(1, 1).getOffsetByInitialPosition()); // { offsetX: -1, offsetY: -1 }再次强调,使用Object.setPrototypeOf() 可能会对性能产生不利影响,因此请确保它仅在必要时使用,并在构造函数声明后立即使用,并在创建任何实例之前使用,以避免对象被“污染”。
备注:设置或更新构造函数可能会导致结果不同且令人困惑的结果。为了防止它,只需在特定情况下定义constructor。多数情况,不使用constructor,并且不需要重新对其赋值。
规范
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-object.prototype.constructor> |