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

瀏覽器相容性

另見