Object.prototype.constructor

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

¥The constructor data property of an Object instance returns a reference to the constructor function that created the instance object. Note that the value of this property is a reference to the function itself, not a string containing the function's name.

注意:这是 JavaScript 对象的属性。对于类中的 constructor 方法,请参见 它自己的参考页面

¥Note: This is a property of JavaScript objects. For the constructor method in classes, see its own reference page.

¥Value

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

¥A reference to the constructor function that created the instance object.

Property attributes of Object.prototype.constructor
Writable yes
Enumerable no
Configurable yes

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

¥Note: This property is created by default on the prototype property of every constructor function and is inherited by all objects created by that constructor.

描述

¥Description

任何对象(null 原型对象 除外)的 [[Prototype]] 上都将具有 constructor 属性。使用文字创建的对象还将具有指向该对象的构造函数类型的 constructor 属性 - 例如,数组文字创建 Array 对象,而 对象字面量 创建普通对象。

¥Any object (with the exception of null prototype objects) will have a constructor property on its [[Prototype]]. Objects created with literals will also have a constructor property that points to the constructor type for that object — for example, array literals create Array objects, and object literals create plain objects.

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 属性。

¥Note that constructor usually comes from the constructor's prototype property. If you have a longer prototype chain, you can usually expect every object in the chain to have a constructor property.

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

示例

¥Examples

显示对象的构造函数

¥Displaying the constructor of an object

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

¥The following example creates a constructor (Tree) and an object of that type (theTree). The example then displays the constructor property for the object theTree.

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

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

此示例显示以下输出:

¥This example displays the following output:

theTree.constructor is function Tree(name) {
  this.name = name;
}

将构造函数属性分配给对象

¥Assigning the constructor property to an object

可以分配非基元的 constructor 属性。

¥One can assign the constructor property of non-primitives.

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]] 上,而不是作为其自己的属性。

¥This does not overwrite the old constructor property — it was originally present on the instance's [[Prototype]], not as its own property.

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

¥But even when Object.getPrototypeOf(a).constructor is re-assigned, it won't change other behaviors of the object. For example, the behavior of instanceof is controlled by Symbol.hasInstance, not constructor:

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

没有任何东西可以保护 constructor 属性不被重新分配或隐藏,因此通常应避免使用它来检测变量的类型,而应使用不太脆弱的方法,例如用于对象的 instanceofSymbol.toStringTag 或用于基元的 typeof

¥There is nothing protecting the constructor property from being re-assigned or shadowed, so using it to detect the type of a variable should usually be avoided in favor of less fragile ways like instanceof and Symbol.toStringTag for objects, or typeof for primitives.

更改构造函数原型的构造函数

¥Changing the constructor of a constructor function's prototype

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

¥Every constructor has a prototype property, which will become the instance's [[Prototype]] when called via the new operator. ConstructorFunction.prototype.constructor will therefore become a property on the instance's [[Prototype]], as previously demonstrated.

但是,如果重新分配 ConstructorFunction.prototype,则 constructor 属性将丢失。例如,以下是创建继承模式的常见方法:

¥However, if ConstructorFunction.prototype is re-assigned, the constructor property will be lost. For example, the following is a common way to create an inheritance pattern:

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

¥The constructor of instances of Child will be Parent due to Child.prototype being re-assigned.

这通常不是什么大问题 - 该语言几乎从不读取对象的 constructor 属性。唯一的例外是使用 @@species 创建类的新实例时,但这种情况很少见,无论如何你都应该使用 extends 语法来子类化内置函数。

¥This is usually not a big deal — the language almost never reads the constructor property of an object. The only exception is when using @@species to create new instances of a class, but such cases are rare, and you should be using the extends syntax to subclass builtins anyway.

然而,当某些调用者使用 constructor 从实例访问原始类时,确保 Child.prototype.constructor 始终指向 Child 本身至关重要。以下面的案例为例:该对象具有 create() 方法来创建自身。

¥However, ensuring that Child.prototype.constructor always points to Child itself is crucial when some caller is using constructor to access the original class from an instance. Take the following case: the object has the create() method to create itself.

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,因此引发了异常。为了避免这种情况,只需分配你将要使用的必要构造函数。

¥In the example above, an exception is thrown, since the constructor links to Parent. To avoid this, just assign the necessary constructor you are going to use.

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 属性时,将属性设置为 non-enumerable 至关重要,因此 constructor 不会在 for...in 循环中被访问 - 通常情况下不会。

¥Note that when manually adding the constructor property, it's crucial to make the property non-enumerable, so constructor won't be visited in for...in loops — as it normally isn't.

如果上面的代码看起来太多样板代码,你也可以考虑使用 Object.setPrototypeOf() 来操作原型链。

¥If the code above looks like too much boilerplate, you may also consider using Object.setPrototypeOf() to manipulate the prototype chain.

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 构造之前,则影响应该很小。

¥Object.setPrototypeOf() comes with its potential performance downsides because all previously created objects involved in the prototype chain have to be re-compiled; but if the above initialization code happens before Parent or CreatedConstructor are constructed, the effect should be minimal.

我们再考虑一个涉及的案例。

¥Let's consider one more involved case.

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

¥For this example to work properly, we can reassign the Parent's static properties to 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 所做的那样。

¥But even better, we can make the constructor functions themselves extend each other, as classes' extends do.

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() 可能会对性能产生不利影响,因此请确保它在构造函数声明之后和创建任何实例之前立即发生 - 以避免对象成为 "tainted"。

¥Again, using Object.setPrototypeOf() may have adverse performance effects, so make sure it happens immediately after the constructor declaration and before any instances are created — to avoid objects being "tainted".

注意:手动更新或设置构造函数可能会导致不同的、有时令人困惑的结果。为了防止这种情况发生,只需在每个特定情况下定义 constructor 的角色即可。大多数情况下,不使用 constructor,也没有必要重新分配它。

¥Note: Manually updating or setting the constructor can lead to different and sometimes confusing consequences. To prevent this, just define the role of constructor in each specific case. In most cases, constructor is not used and reassigning it is not necessary.

规范

Specification
ECMAScript Language Specification
# sec-object.prototype.constructor

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看