JavaScriptの関数は原則的に、生成された瞬間にprototype
プロパティを持つ。
その中身はオブジェクトであり、constructor
プロパティのみを持つ。このプロパティは、関数自身を参照している。
function myFunc(){}; console.log(Object.getOwnPropertyNames(myFunc)); // [ 'length', 'name', 'arguments', 'caller', 'prototype' ] // 明示しなくても、上記のプロパティを持つ console.log(Object.getOwnPropertyNames(myFunc.prototype)); // [ 'constructor' ] // prototypeが持っているプロパティはconstructorのみ console.log(myFunc.prototype.constructor === myFunc); // true // prototypeのconstructorは、myFuncを参照している
このような仕組みであるため、インスタンスのconstructor
プロパティは通常、コンストラクタを返す。
function myFunc(){}; console.log(Object.getOwnPropertyNames(myFunc)); // [ 'length', 'name', 'arguments', 'caller', 'prototype' ] console.log(Object.getOwnPropertyNames(myFunc.prototype)); // [ 'constructor' ] console.log(myFunc.prototype.constructor === myFunc); // true // prototypeのconstructorは、myFuncを参照している var myInstance = new myFunc(); console.log(myInstance.constructor === myFunc); // true console.log(myInstance.hasOwnProperty('constructor')); // false console.log(myInstance.__proto__.hasOwnProperty('constructor')); // true // myInstance自身はconstructorプロパティを持っていないため、 // プロトタイプのconstructorプロパティを参照する。
しかし、あくまでも参照先のプロトタイプオブジェクトの値を返しているだけなので、インスタンス自身にプロパティを定義したり、プロトタイプオブジェクト自体を書き換えた場合は、挙動が変わってくる。
function Person(){}; var person1 = new Person(); // インスタンスのconstructorは通常、コンストラクタを指すが…… console.log(person1.constructor === Person); // true person1.constructor = 'foo'; console.log(person1.constructor === Person); // false // インスタンス自身にconstructorプロパティを追加すると当然、 // そちらを参照するようになる。 Person.prototype = { someFunc: function(){} }; var person2 = new Person(); console.log(person2.constructor === Person); // false console.log(person2.constructor === Object); // true // プロトタイプオブジェクトを上書きすると、 // constructorプロパティはObjcetを参照するようになる。 // 新しく定義したPerson.prototypeはconstructorを持っていないため、 // プロトタイプオブジェクトのconstructorを参照する。 // そこにはObjectが格納されているため、このような挙動になる。 console.log(Person.prototype.hasOwnProperty('constructor')); // false console.log(Person.prototype.__proto__.hasOwnProperty('constructor')); // true console.log(Person.prototype.__proto__.constructor === Object); // true // プロトタイプオブジェクトを上書きする際に改めてconstructorプロパティを定義すれば、 // 上記のような挙動は防げる。 Person.prototype = { constructor: Person, someFunc: function(){} }; var person3 = new Person(); console.log(person3.constructor === Person); // true
また、.prototype.constructor
は他のプロパティ同様、デフォルトでは書き換え可能になっているため、この値を変更してしまうことも可能。
さらに、操作可能でもあるため、削除してしまうことも可能になっている。
function Person(){}; var person1 = new Person(); console.log(person1.constructor === Person); // true console.log(Object.getOwnPropertyDescriptor(Person.prototype, 'constructor')); // { value: [Function: Person], // writable: true, // enumerable: false, // configurable: true } // 操作可能、かつ書き込み可能であることが分かる // 書き換え Person.prototype.constructor = 'hoge'; console.log(person1.constructor === Person); // false console.log(person1.constructor === 'hoge'); // true // プロトタイプから削除 delete Person.prototype.constructor; console.log(person1.constructor === 'hoge'); // false console.log(person1.constructor === Object); // true console.log(Person.prototype.__proto__ === Object.prototype); // true console.log(Person.prototype.__proto__.constructor === Object); // true // プロトタイプのプロトタイプからも削除 delete Person.prototype.__proto__.constructor; console.log(person1.constructor === Object); // false console.log(person1.constructor === undefined); // true
上記の例では、プロトタイプチェーンの終端(Object.prototype
)まで辿ってconstructor
を削除したため、
person1.constructor === undefined
となる。