Object.prototype.constructor

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

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

注意:这是 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(); // Inheritance: TypeError -> Error -> Object
const proto = Object.getPrototypeOf;

Object.hasOwn(o, "constructor"); // false
proto(o).constructor === TypeError; // true
proto(proto(o)).constructor === Error; // true
proto(proto(proto(o))).constructor === Object; // true

示例

显示对象的构造函数

以下示例创建一个构造函数(Tree)以及该类型的一个对象(theTree)。然后,示例显示对象 theTreeconstructor 属性。

js
function Tree(name) {
  this.name = name;
}

const theTree = new Tree("Redwood");
console.log(`theTree.constructor is ${theTree.constructor}`);

此示例显示以下输出

theTree.constructor is 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

// etc.

这不会覆盖旧的 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 — the instance property shadows the one on its prototype

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

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

没有任何东西可以阻止 constructor 属性被重新分配或覆盖,因此在使用它来检测变量类型时,通常应避免使用,而应采用更不易出错的方法,例如 instanceof 和对象的 Symbol.toStringTag,或者原始值的 typeof

更改构造函数原型的 constructor

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

但是,如果重新分配了 ConstructorFunction.prototypeconstructor 属性将会丢失。例如,以下是创建继承模式的常用方法。

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

function Child() {
  Parent.call(this); // Make sure everything is initialized properly
}
// Pointing the [[Prototype]] of Child.prototype to Parent.prototype
Child.prototype = Object.create(Parent.prototype);

由于 Child.prototype 被重新分配,Child 实例的 constructor 将是 Parent

这通常不是什么大问题——语言几乎从不读取对象的 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, since constructor === Parent

在上面的示例中,会抛出一个异常,因为 constructor 链接到了 Parent。为了避免这种情况,只需分配你将要使用的必要的构造函数。

js
function Parent() {
  // …
}
function CreatedConstructor() {
  // …
}

CreatedConstructor.prototype = Object.create(Parent.prototype, {
  // Return original constructor to Child
  constructor: {
    value: CreatedConstructor,
    enumerable: false, // Make it non-enumerable, so it won't appear in `for...in` loop
    writable: true,
    configurable: true,
  },
});

CreatedConstructor.prototype.create = function () {
  return new this.constructor();
};

new CreatedConstructor().create().create(); // it's pretty fine

请注意,在手动添加 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(); // still works without re-creating constructor property

Object.setPrototypeOf() 存在潜在的性能问题,因为它需要重新编译原型链中所有先前创建的对象;但如果上述初始化代码发生在 ParentCreatedConstructor 被构造之前,影响应该是最小的。

让我们考虑一个更复杂的案例。

js
function ParentWithStatic() {}

ParentWithStatic.startPosition = { x: 0, y: 0 }; // Static member property
ParentWithStatic.getStartPosition = function () {
  return this.startPosition;
};

function Child(x, y) {
  this.position = { x, y };
}

Child.prototype = Object.create(ParentWithStatic.prototype, {
  // Return original constructor to Child
  constructor: {
    value: Child,
    enumerable: false,
    writable: true,
    configurable: true,
  },
});

Child.prototype.getOffsetByInitialPosition = function () {
  const position = this.position;
  // Using this.constructor, in hope that getStartPosition exists as a static method
  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, since the
// constructor is Child, which doesn't have the getStartPosition static method

为了使此示例正常工作,我们可以将 Parent 的静态属性重新分配给 Child

js
// …
Object.assign(Child, ParentWithStatic); // Notice that we assign it before we create() a prototype below
Child.prototype = Object.create(ParentWithStatic.prototype, {
  // Return original constructor to Child
  constructor: {
    value: Child,
    enumerable: false,
    writable: true,
    configurable: true,
  },
});
// …

但更好的是,我们可以让构造函数本身相互继承,就像类的 extends 所做的那样。

js
function ParentWithStatic() {}

ParentWithStatic.startPosition = { x: 0, y: 0 }; // Static member property
ParentWithStatic.getStartPosition = function () {
  return this.startPosition;
};

function Child(x, y) {
  this.position = { x, y };
}

// Properly create inheritance!
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 不会被使用,重新分配它也不是必需的。

规范

规范
ECMAScript® 2026 语言规范
# sec-object.prototype.constructor

浏览器兼容性

另见