constructor プロパティについて
説明をつけておくと、constructor プロパティは、オブジェクト作成時に自動的に生成されるプロパティで、そのオブジェクトが生成されたときに使われたコンストラクタが参照されています。
ミックスインパターン new this.constructor() の利用 - わからん
たまたま見つけたので引用するけど、何処か別のところでも似たような勘違いが書かれていたように思う。もしかすると、このように勘違いしている人は多いのかもしれない。
正しておきたいと思う次第。
間違っているのはconstructor プロパティは、オブジェクト作成時に自動的に生成されるプロパティ
という部分。
constructor プロパティが生成されるのは、Function オブジェクトが生成されたときです。
以下は、Function オブジェクトが作られる時の処理の流れの一部です。
13.2 Creating Function Objects - Annotated ES5
Let proto be the result of creating a new object as would be constructed by the expression
new Object()
whereObject
is the standard built-in constructor with that name.Call the [[DefineOwnProperty]] internal method of proto with arguments
"
constructor
"
, Property Descriptor {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false.Call the [[DefineOwnProperty]] internal method of F with arguments
"
prototype
"
, Property Descriptor {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, and false.
簡略化して書くと以下の様な感じ
- prototype プロパティとなるオブジェクト、protoを new Object で生成
- protoにconstrctorプロパティを設置
- 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.constructor
がObject
になってしまう理由が分かります。
- Foo Functionオブジェクトの生成
- Foo.prototype.constructorの生成
- Foo.prototypeに
{ getName: function(){ return this.name; } }
の代入 - fooInstance に
new Foo
- 新たなオブジェクトの[[prototype]]に
Foo.prototype
を設置
- 新たなオブジェクトの[[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