constructor プロパティについて

説明をつけておくと、constructor プロパティは、オブジェクト作成時に自動的に生成されるプロパティで、そのオブジェクトが生成されたときに使われたコンストラクタが参照されています。

ミックスインパターン new this.constructor() の利用 - わからん

たまたま見つけたので引用するけど、何処か別のところでも似たような勘違いが書かれていたように思う。もしかすると、このように勘違いしている人は多いのかもしれない。

正しておきたいと思う次第。

間違っているのはconstructor プロパティは、オブジェクト作成時に自動的に生成されるプロパティという部分。

constructor プロパティが生成されるのは、Function オブジェクトが生成されたときです。

以下は、Function オブジェクトが作られる時の処理の流れの一部です。

  1. Let proto be the result of creating a new object as would be constructed by the expression new Object()where Object is the standard built-in constructor with that name.

  2. Call the [[DefineOwnProperty]] internal method of proto with arguments "constructor", Property Descriptor {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false.

  3. Call the [[DefineOwnProperty]] internal method of F with arguments "prototype", Property Descriptor {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, and false.

13.2 Creating Function Objects - Annotated ES5

簡略化して書くと以下の様な感じ

  1. prototype プロパティとなるオブジェクト、protoを new Object で生成
  2. protoconstrctorプロパティを設置
  3. Functionオブジェクトにprototypeプロパティとしてprotoを設置

つまり

var func = function Foo() {
};

とした時点で、

func.prototype.constructor === func // => true

となっているわけ。

決して、new func した時に、生成されるわけではないのです。

ついでに、上記処理を踏まえると、

function Foo() {}
Foo.prototype = {
  getName: function() {
    return this.name;
  },
};

var fooInstance = new Foo;
console.log(fooInstance.constructor)

としたとき、fooInstance.constructorObjectになってしまう理由が分かります。

  1. Foo Functionオブジェクトの生成
    • Foo.prototype.constructorの生成
  2. Foo.prototype{ getName: function(){ return this.name; } } の代入
  3. fooInstancenew Foo
    • 新たなオブジェクトの[[prototype]]にFoo.prototypeを設置

1.の時点では、Foo.prototype.constructor は Foo を示しているが、2.でprototypeに代入してしまっているので、Foo.prototype.constructor は({ getName: function(){ return this.name; } }).constructor、つまり、Object を示す。ということになります。

よって、

説明をつけておくと、constructor プロパティは、オブジェクト作成時に自動的に生成されるプロパティで、そのオブジェクトが生成されたときに使われたコンストラクタが参照されています。

の後半部分も必ずしもそうなるとは限らないのです。

obj.constructor は必ず生成もとのコンストラクタ関数を示すわけではない、非常に不確かなものなのです。"JavaScript sucks" と言っても良いでしょう。

これは結構困ったことなので、先人たちは

function Foo(){}
Foo.prototype.getName = function(){ return this.name; }

などと、一つ一つプロパティを代入するとか

function extends(constructor, proto) {
  var keys = Object.getOwnPropetyNames(proto);
  var i, len, key;
  for (i = 0, len = keys.length; ++i) {
    key = keys[i];
    Object.defineProperty(constructor.prototype, key, Object.getOwnPropertyDescriptor(proto, key));
  }
}

function Foo () {}
exntends(Foo, { getName: function(){return this.name;} });

などと、一つユーティリティ関数を用意するとか、工夫をしてきたわけです。

:wq