Object.prototype.constructor
BaselineWidely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
constructor
はObject
インスタンスのデータプロパティで、インスタンスオブジェクトを生成したコンストラクター関数への参照を返します。なお、このプロパティの値は関数そのものへの参照であり、関数名を含んだ文字列ではありません。
メモ:これは JavaScript オブジェクトのプロパティです。クラスのconstructor
メソッドについては、リファレンスページを参照してください。
値
インスタンスオブジェクトを作成したコンストラクター関数への参照。
Object.prototype.constructor のプロパティ属性 | |
---|---|
書込可能 | 可 |
列挙可能 | 不可 |
設定可能 | 可 |
メモ:このプロパティは、既定ではすべてのコンストラクター関数のprototype
プロパティに作成され、そのコンストラクターで作成されたすべてのオブジェクトに継承されます。
解説
(null
プロトタイプオブジェクトを除く)どんなオブジェクトも、その[[Prototype]]
にコンストラクターのプロパティを持っています。例えば、配列リテラルは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;Object.hasOwn(o, "constructor"); // falseproto(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 is ${theTree.constructor}`);
この例の出力は次のとおりです。
theTree.constructor is 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// etc.
これは古い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
の動作はconstructor
ではなく、Symbol.hasInstance
によって制御されます。
const arr = [];arr.constructor = String;arr instanceof String; // falsearr instanceof Array; // true
constructor
プロパティは、再代入されたりシャドウ化されたりしないように保護するものは何もありません。したがって、変数の型を検出するためにコンストラクターを使用することは通常避けるべきで、オブジェクトの場合はinstanceof
やSymbol.toStringTag
、プリミティブの場合はtypeof
のような壊れにくい方法を選ぶべきです。
コンストラクター関数のプロトタイプのコンストラクターを変更
すべてのコンストラクターはprototype
プロパティを持っており、このプロパティはnew
演算子で呼び出されたときにインスタンスのプロトタイプ ([[Prototype]]
) になります。従って、コンストラクター関数ConstructorFunction.prototype.constructor
は前回示したように、インスタンスの[[Prototype]]
のプロパティになります。
しかし、ConstructorFunction.prototype
に再代入されると、constructor
プロパティは失われます。例えば、次の例は継承パターンを作成する一般的な方法です。
function Parent() { // …}Parent.prototype.parentMethod = function () {};function Child() { Parent.call(this); // すべてが正しく初期化されていることを確認}// 子のプロトタイプを親のプロトタイプで再定義Child.prototype = Object.create(Parent.prototype);
Child
のインスタンスのconstructor
はChild.prototype
が再割り当てされるため、Parent
になります。
言語がオブジェクトの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 は 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(); //コンストラクタープロパティを再作成することなく動作する
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, { // Return original constructor to Child constructor: { value: Child, enumerable: false, writable: true, configurable: true, },});Child.prototype.getOffsetByInitialPosition = function () { const position = this.position; // getStartPosition が静的メソッドとして存在することを期待して this.constructor を使用 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
に再割り当てします。
// …Object.assign(Child, ParentWithStatic); // Notice that we assign it before we create() a prototype belowChild.prototype = Object.create(ParentWithStatic.prototype, { // Return original constructor to 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 };}// 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
使用されず、再割り当ての必要はありません。
仕様書
Specification |
---|
ECMAScript® 2026 Language Specification # sec-object.prototype.constructor |