プロトタイプチェーン — 自分に無ければ、上に聞く
JavaScript で dog.toString() と書けてしまうのは不思議です。dog という犬のオブジェクトに toString なんて自分では定義していないのに、なぜか動く。その秘密がプロトタイプチェーンです。オブジェクトは「自分に無いプロパティを聞かれたら、__proto__ という上の階に問い合わせる」という鎖でつながっています。
探し方はとてもシンプル。まず自分を見て、無ければ1つ上へ、それでも無ければさらに上へ。見つかった瞬間に止まり、終端の null まで行っても無ければ undefined です。下の図1で、アクセスしたいプロパティを選んでみてください。鎖を1段ずつ辿る様子が見えます。
dog から探し始めます。自分に無ければ __proto__ をたどって1段ずつ上へ。見つかったらそこで止まります。
dog.fly() は終端まで行っても見つからないなぜこの仕組みが嬉しいのか
もし犬を100匹作るたびに bark や toString を各オブジェクトへコピーしていたら、同じ関数が100個分メモリに散らばります。プロトタイプチェーンなら、共通の機能は Dog.prototype に1つ置くだけ。すべての犬がそれを「上の階」として共有します。class 構文で書くメソッドも、裏側ではこの prototype に収められています。
探索は下から上への一方通行です。だから自分の階に同じ名前のプロパティを持つと、上の階の同名プロパティは隠れて(シャドーイング)到達されません。図1の dog.toString() のように、最後まで辿ると必ず Object.prototype に行き着くのも、ほぼすべてのオブジェクトがそこを大もとに持つからです。なお「自分自身が持つか」だけを確かめたいときは hasOwnProperty を使い、鎖をたどらせません。
- prototype
- 関数(コンストラクタ)が持つ、インスタンス共通の置き場所。メソッドはここに入る。
- __proto__
- 各オブジェクトが指す「1つ上の階」への参照。探索はこれをたどって進む。
- シャドーイング
- 自分の階に同名プロパティがあると、上の階の同名プロパティが隠れて見えなくなること。
- Object.prototype
- 鎖のほぼ終端にある大もと。toString など共通機能が置かれている。
まとめ
プロトタイプチェーンは「自分に無ければ上へ問い合わせる」という、たった1つのルールでできた継承の仕組みです。共通機能を1か所に置いて全インスタンスで共有でき、メモリも節約できます。図1で dog.eat() を選ぶと2段、dog.toString() なら3段上って見つかり、dog.fly() は終端まで行って undefined になる —— 速さも継承も、この鎖を辿る動きから生まれているのです。