entry
prototypeとnew
JavaScript
prototypeについて、まだ理解できなかったのでいろいろ調べまわった
だいぶわかってきたぞ。
hasOwnPropertyで調べてみる
JavaScriptの全てのオブジェクトには、hasOwnPropertyというメソッドが定義されている。
var Class = function(){}
Class.prototype = {
hoge : 'piyo'
};
var obj = new Class();
alert(obj.hasOwnProperty(`hoge`));//(1-1)false
obj.hoge = 'fuga';
alert(obj.hasOwnProperty(`hoge`));//(1-2)true
hasOwnPropertyを使うと、そのオブジェクトにメンバがあるかどうかを返す。
(1-1)の時点では、obj.prototypeにプロパティhogeがあるだけで、objには無く、(1-2)ではobjにhogeがあるのでtrue。
この例だと最終的には、objにもobj.prototypeにもhogeがあるということ。
new演算子の動き
なんかちょっとわかりやすいのみつけたのです。
prototype図示とnewの挙動 - web newbie
からたどって。。
JavaScriptのnew演算子の意味 - Days on the Moon
ここらから引用と自分なりに勉強して書かせていただきました。っと、このあたりを見つけたので今回のエントリ書けたです。感謝。
prototype指向はこうするとわかりやすい! - web newbie
- 1:prototypeとオブジェクトの関連を図でイメージする。
- 2:newの挙動を把握する。
new演算子の挙動 - Days on the Moon
JavaScript における new 演算子の動作は大まかにいって以下のとおりである。(new F() とした場合。)
- 1:新しいオブジェクトを作る。
- 2:1 で作ったオブジェクトの [[Prototype]] 内部プロパティ (__proto__ プロパティ) に F.prototype の値を設定する。
- F.prototype の値がオブジェクトでないのなら代わりに Object.prototype の値を設定する。
- 3;F を呼び出す。このとき this の値は 1 で作ったオブジェクトとし、引数には new 演算子とともに使われた引数をそのまま用いる。
- 4:3 の返り値がオブジェクトならそれを返す。そうでなければ 1 で作ったオブジェクトを返す。
ここで「オブジェクトである」というのはプリミティブ値 (文字列、数値、真偽値、undefined 、null) ではないということだ。
new String("string") 、new Number(123) 、new Boolean(true) はオブジェクトだが "string" 、123 、true はオブジェクトではない。
というわけで。。
コード見ながらnewの動きを整理
var Class = function(){}
Class.prototype = {
hoge : 'piyo'
};
var obj = new Class(); //(2-1)
alert(obj.hasOwnProperty(`hoge`)); //(2-2)false
obj.hoge = 'fuga'; //(2-3)
alert(obj.hasOwnProperty(`hoge`)); //(2-4)true
- 1: (2-1)でnewすると、新しいオブジェクト{}を作る。
- 2: 作ったオブジェクトの__proto__にClassのprototypeが格納している参照を格納。
ここでClass.prototypeがオブジェクトでないなら、Object.prototype(の参照)を格納。ここではClass.prototypeはオブジェクト{hoge:'piyo'}なのでこれの参照を格納する。
- 3: 関数Class()を呼び出し。
ここでthisには新しく作ったオブジェクト{}への参照が格納される。newした時に与えられた引数でClass()を実行
- 4: 全てが終わったら、newはオブジェクト{}への参照を返す。
という感じ?
prototypeと__proto__は分けて考えたほうが良いらしいです。前者は関数オブジェクトの雛形。後者は作られたオブジェクトが実行時に参照するときのもの。なるほど。(なので、上でobj.prototypetと書いてたのは、obj.__proto__と記述したほうが良いのかな。)
図で示すとわかりやすいprototype
こんなコード。ほげほげうるさいけれど我慢w
var Class = function(){};
Class.prototype = { prop : 'hogehoge' };
var hoge = new Class(); //(3-1)
alert(hoge.prop); //hogehogeと出力
Class.prototype = { prop : 'piyopiyo' };
var piyo = new Class(); //(3-2)
alert(piyo.prop); //piyopiyoと出力
var fuga = new Class(); //(3-3)
Class.prototype = { prop : 'fugafuga' };
alert(fuga.prop); //(3-5)piyopiyoと出力
fuga.__proto__.prop = 'piyofuga';
alert(hoge.prop); //hogehogeと出力
alert(piyo.prop); //piyofugaと出力
alert(fuga.prop); //piyofugaと出力
の、オブジェクトの様子を図示してみた。
黒線がnewによるオブジェクトの作成。各色の線が各参照先を示す。数字はオブジェクト作成やチェーンが作られる順番。
(3-1)でvar hoge作成時に、Class.prototypeが指しているのは、{ prop : 'hogehoge' }で、hoge.__proto__はこれを参照する。
var piyoも同様(3-2)
ここで、var fugaの作成時(3-3)に、Class.prototypeが指しているのは{ prop : 'piyopiyo' }のままなので、fuga.propは'fugafuga'でなく、'piyopiyo'となる(3-5)ということ。
((3-4)で{ prop : 'fugafuga' }を作っているが、これを参照するつながりはできていない。)
その後、fuga.__proto__.propの値を変更すると、{ prop : 'piyopiyo' }オブジェクトのpropプロパティを変更することになる。すると、fugaだけでなく、piyoの__proto__も同じオブジェクトを指しているので、piyo.propも'piyofuga'になる。ということ。
ちなみに、(3-4)でClass.prototypeは{ prop : 'fugafuga' }を参照するようになるが、このあとnewでオブジェクトが作られないかぎり{ prop : 'fugafuga' }を__proto__で参照するオブジェクトは存在しないことになる。
大事なのは、{}も、(どこかよくわからん場所に作られる名前の無い)オブジェクトなんだということか。
だいぶわかってきた!
でも、サンプルコードがわかりにくかったか。。あとは実践だな。
参考
(勝手に)参考にさせていただいてありがとうございます。
comments & trackbacks
comments
trackbacks
please entry coment or trackback
comment form for this entry
Trackback URL this entry
http://color-chips.xsrv.jp/mt5/mt-tb.cgi/1168