◆ __proto__の参照でつながってるオブジェクト

JavaScriptで重要なのもの それは プロトタイプ です
ということで プロトタイプチェーンと__proto__についてです

とりあえずこの図をご覧くださいませ~

protochain


まー こういうことです
見たままです

説明は以上!














と言うと 適当すぎる気もするのでもうちょっと詳しく書いていきます

objという変数があります
objにはオブジェクトが入っていて {x:20,y:30,z:40}です

また __proto__というプロパティもあって それには {a:100} のオブジェクトが入っています

このときに obj.x のデータはもちろん 20 です
では obj.a はなんでしょう?

普通にobjの中身をみるとaというプロパティはないので undefined と思うかもです
ですが JavaScriptでは オブジェクトの中にアクセスされたプロパティがないと __proto__ のオブジェクトからそのプロパティを探します

なので obj.a100になります

__proto__ の先がオブジェクトならさらにその先にも __proto__ があります
この例だと obj.a はすぐに見つかりましたが 見つからなければずっと __proto__ をたどっていきます
__proto__ でオブジェクトがつながっていることをプロトタイプチェーンと呼ぶようです


では {a:100} のオブジェクトにも __proto__ があって図のようになってたとします

どのオブジェクトも作られた時は __proto__Object.prototype が入っています
なのでチェーンしてても基本的に最後が Object.prototype です
Object.prototype__proto__ nullです

プロトタイプチェーンを辿っていっても最後までプロパティが見つからない obj.b などはundefined になります

obj.a500を代入すると obj(図の左下のx:20などがある四角)に a:500 が追加されます
そうなると 最初の段階で obj.a が見つかるので a:100 まで行かずに結果500が返ってきます

obj1 = {x:50} obj2 = {a:100} obj3 = {x:20, y:30, z:40} obj3.__proto__ = obj2 obj2.__proto__ = obj1 obj3.x 20 obj3.__proto__.__proto__.x 50 obj3.a 100 // obj3.aに上書き obj3.a = "上書き?" obj3.a "上書き?" // 元々obj3.__proto__.aを見ていたので上書きはされず前のが見れる obj3.__proto__.a 100 // "上書き?"はobj3.aのプロパティなので完全に上書きされる obj3.a = "上書き!" obj3.a "上書き!" // ループさせてみる obj1.__proto__ = obj3 Uncaught Error: Cyclic __proto__ value

プロトタイプチェーンは当然ながらループは出来ません
ループができたら存在しないプロパティの探索が終わらないですからね

__proto__ は直接プロパティとしてアクセスもできるのですが アクセスする用のメソッドが用意されてます
Object.getPrototypeOf Object.setPrototypeOf です

使い方は↓のとおりです
function Test(){} Test.prototype.abc = 100 x = new Test x.__proto__ Test {abc: 100} // getPrototypeOfは__proto__にアクセスするのと一緒 Object.getPrototypeOf(x) Test {abc: 100} // setPrototypeOfで__proto__に代入と一緒 Object.setPrototypeOf(x, {xyz:1}) x.__proto__ Object {xyz: 1}

聞くところによると この記事の内容のほとんどがECMA5かららしいんですよねー
それまでどうしてたのかは知らないです
私がJavaScript書き始めたのがECMA5からですので

おまけ

プロトタイプチェーンの最後はObject.prototypeと決まってましたが その流れを断ち切ることも可能です
function A(){} // __proto__ を Object.prototype から null にする A.prototype.__proto__ = null a = new A // Objectのインスタンスじゃなくなる! a instanceof Object false // toStringがない! a.toString undefined // toStringが必要な計算をすると 1 + a Uncaught TypeError: Cannot convert object to primitive value // 普通のオブジェクトだと 1 + {} "1[object Object]" Object.prototype.__proto__ null A.prototype.__proto__ undefined

そうすると Object のインスタンスでなくなり 絶対あるはずのtoStringメソッドもなくなります
toStringがないのでtoStringが必要になる計算をするとエラーが発生します

通常 チェーンの最後(Object.prototype.__proto__) の値はnullです
自分でチェーンを切るために null を入れてもなぜか undefined になってしまいます

元々のものなのかプログラム書いた人がわざと切ったかをわかるようにしてくれてるようです
わざと切る理由が全くわからないですけどね