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 July 2015.

Object 实例的 constructor 数据属性返回一个引用,指向创建该实例对象的构造函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。

备注: 这是 JavaScript 对象的一个属性。关于类的 constructor 方法,请参见其参考页面

对创建该实例对象的构造函数的引用。

Object.prototype.constructor 的属性特性
可写
可枚举
可配置

备注: 这个属性默认会在每个构造函数的 prototype 属性上创建,并由该构造函数创建的所有对象继承。

描述

除了 null 原型对象之外,任何对象都会在其 [[Prototype]] 上有一个 constructor 属性。使用字面量创建的对象也会有一个指向该对象构造函数类型的 constructor 属性,例如,数组字面量创建的 Array 对象和对象字面量创建的普通对象。

js
const o1 = {};
o1.constructor === Object; // true

const o2 = new Object();
o2.constructor === Object; // true

const a1 = [];
a1.constructor === Array; // true

const a2 = new Array();
a2.constructor === Array; // true

const n = 3;
n.constructor === Number; // true

请注意,constructor 属性通常来自构造函数的 prototype 属性。如果你有一个更长的原型链,通常可以假定链中的每个对象都有一个 constructor 属性。

js
const o = new TypeError(); // 继承关系:TypeError -> Error -> Object
const proto = Object.getPrototypeOf;
proto(o).constructor === TypeError; // true
proto(proto(o)).constructor === Error; // true
proto(proto(proto(o))).constructor === Object; // true

示例

打印对象的构造函数

下面这个示例创建一个构造函数(Tree),以及该类型的对象(theTree)。然后打印了 theTree 对象的 constructor 属性。

js
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 属性赋值。

js
const arr = [];
arr.constructor = String;
arr.constructor === String; // true
arr instanceof String; // false
arr instanceof Array; // true

const foo = new Foo();
foo.constructor = "bar";
foo.constructor === "bar"; // true

// 等等…

这不会覆盖旧的 constructor 属性——它实际上存在于实例的 [[Prototype]] 中,而不是作为其自有属性。

js
const arr = [];
Object.hasOwn(arr, "constructor"); // false
Object.hasOwn(Object.getPrototypeOf(arr), "constructor"); // true

arr.constructor = String;
Object.hasOwn(arr, "constructor"); // true——实例属性会覆盖原型链上的同名属性

但是,即使对 Object.getPrototypeOf(a).constructor 重新赋值,它也不会改变对象的其他行为。例如,instanceof 的行为由 Symbol.hasInstance 控制,而不是由 constructor 控制:

js
const arr = [];
arr.constructor = String;
arr instanceof String; // false
arr instanceof Array; // true

constructor 属性没有受到保护,可以被重新赋值或被覆盖,因此在检测变量类型时,通常应避免使用它,而应该使用更不易出错的方法,如对于对象使用 instanceofSymbol.toStringTag,对于基本类型使用 typeof

更改构造函数原型对象的 constructor 属性

每个构造函数都有一个 prototype 属性,当通过 new 运算符调用时,该属性将成为实例的 [[Prototype]]。因此,ConstructorFunction.prototype.constructor 将成为实例的 [[Prototype]] 上的属性,如前面所述。

然而,如果对 ConstructorFunction.prototype 重新赋值,constructor 属性将丢失。例如,以下是创建继承模式的常见方式:

js
function Parent() {
  // …
}
Parent.prototype.parentMethod = function () {};

function Child() {
  Parent.call(this); // 确保所有内容都已正确初始化
}
// 将 Child.prototype 的 [[Prototype]] 指向 Parent.prototype
Child.prototype = Object.create(Parent.prototype);

由于重新赋值了 Child.prototypeChild 实例的 constructor 将是 Parent

通常情况下,这不是什么大问题——JavaScript 几乎从不读取对象的 constructor 属性。唯一的例外是在使用 [Symbol.species] 创建类的新实例时,但这种情况很少见,并且你应该使用 extends 语法来子类化内置对象。

然而,在某些调用使用 constructor 从实例中访问原始类时,确保 Child.prototype.constructor 总是指向 Child 本身非常重要。考虑这种情况:对象具有 create() 方法来创建自身。

js
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。为了避免这种情况,只需将其赋值为你将要使用的必要构造函数即可。

js
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() 来操作原型链。

js
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() 存在潜在的性能缺陷,因为所有先前创建的涉及该原型链的对象都必须重新编译;但是,如果上述初始化代码发生在 ParentCreatedConstructor 构造之前,其影响应该是很小的。

接下来,看另外一个相关示例。

js
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 的构造分配静态属性:

js
// …
Object.assign(Child, ParentWithStatic); // 注意,在创建 Child 的原型前我们先分配它的值
Child.prototype = Object.create(ParentWithStatic.prototype, {
  // 将原始构造函数返回给 Child
  constructor: {
    value: Child,
    enumerable: false,
    writable: true,
    configurable: true,
  },
});
// …

但更好的方法是,我们可以使构造函数本身相互继承,就像类的 extends 一样。

js
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® 2025 Language Specification
# sec-object.prototype.constructor

浏览器兼容性

Report problems with this compatibility data on GitHub
desktopmobileserver
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
Deno
Node.js
constructor

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support

参见