Fn.prototypeを直接置き換えてはダメ
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 1
◆ prototypeに元々あるconstructorが消えてしまいます
◆ 自分で設定するか 消えて困らないならやってもいいんじゃないかなと思います
◆ 自分で設定するか 消えて困らないならやってもいいんじゃないかなと思います
prototypeに追加する方法
function A(){}
A.prototype.fn = function(){}
function B(){}
B.prototype = {
fn: function(){}
}
この2種類ですAの方は 1つずつ追加しますが Bではまとめてオブジェクトを作って置き換えてしまいます
どっちでもよさそうですし どっちでも良いと書かれているところも多いですが 重要な違いがあります
Bの方法では自分で作ったオブジェクトに置き換えるのでconstructorという最初からB.prototypeにあるプロパティを消してしまいます
JavaScriptのconstructorはそのインスタンスを作るのに使った関数への参照が入っています
それ以外でもAやBの方法でprototypeを変更した後に さらに追加・変更したいときにBを使うと 当たり前ですが それまでに追加したプロパティが消えてしまいます
constructorが消えると?
a = new A
A {fn: function}
__proto__: A
b = new B
B {fn: function}
__proto__: Object // Bじゃない
a.constructor
function A(){}
b.constructor
function Object() { [native code] }
a.constructor === A
true
b.constructor === B
false // constructorが変わったからfalse
a instanceof A
true
a instanceof B
false
b instanceof A
false
b instanceof B
true // trueになる
a
A {fn: function}
__proto__: A
constructor: function A(){}
fn: function (){}
__proto__: Object
b
B {fn: function}
__proto__: Object // ここもBじゃない
fn: function (){}
__proto__: Object
a instanceof Object
true
b instanceof Object
true
インスタンスを作った時に a.constructorはAですが b.constructorはBではなくObjectになりますB.prototypeにconstructorがないので プロトタイプチェーンの先のObject.prototypeのconstructorを見ています
constructorプロパティを使ってその関数で作ったインスタンスかを確認するとfalseになってしまいます
instanceof を使えばconstructorが変わっていても大丈夫ですがプロトタイプチェーンのどこかに入っていればそのインスタンスだと扱われるので注意が必要です
instanceofについて詳しく
例をあげると aもbもObject関数のインスタンスということになります
自分でconstructorを追加する
function C(){}
C.prototype = {
fn: function(){},
constructor: C
}
C {fn: function, constructor: function}
c = new C
C {fn: function, constructor: function} // constructorが見える
__proto__: C
constructor: function C(){}
fn: function (){}
__proto__: Object
Object.defineProperty(C.prototype, "constructor", {enumerable:false})
C {fn: function}
c2 = new C
C {fn: function} // ここがfnだけになってる
__proto__: C
constructor: function C(){} // ←薄くなってる
fn: function (){}
__proto__: Object
どうしてもB.prototype = {}形式で書きたいけどconstructorもいる ってときは自分でconstructorを付け足せばいいんですですが単純にプロパティにいれるだけでは 元々のconstructorとちょっと挙動が変わっています
Chromeの開発者ツールで言うと 薄い色になってない です
この状態だとfor inでオブジェクトの中身一覧をみたときに constructorまで見えてしまいます
そこで元々のconstructorと同じようにするために Object.defineProperty を使ってenumerable属性をfalseに書き換えます
これで やっと1つずつprototypeにプロパティを追加したのと同じ動きができるようになります
__proto__もあるけど
prototypeには__proto__というプロパティも最初からありますしかし これはObject.prototypeへの参照が入っていて 新しくオブジェクトを作って書き換えても__proto__は存在して Object.prototypeへの参照が入っているので基本的に問題はありません
一応 例外が2パターンあります
● 上書きするオブジェクトが{ } で作ったものじゃなく 何かのインスタンスのとき
新しい__proto__がObject.prototypeじゃないので必要があるなら自分で再設定が必要です
● 上書き前の__proto__がObject.prototype じゃないとき
普通 関数定義時のprototypeの__proto__は絶対Object.prototypeになっています
なっていないときはクラスの継承のようなことをして自分で(自分が知らないなら継承するために使ったライブラリの関数が)__proto__を書き換えています
なので最初の方に書いた「AやBの方法でprototypeを変更した後に さらに追加・変更したいときにBを使うと 当たり前ですが それまでに追加したプロパティが消えてしまいます」に当たることをやってるわけです
こんな特殊なことをしなければ普段気にすることはない程度です
__proto__が気になる人はココで詳しく書いてます
そういうわけですのでprototypeを上書きするのならconstructorに注意してください