図で理解するJavaScriptのプロトタイプチェーン

JavaScriptのリファレンスといえば[MDN](https://developer.mozilla.org/ja/)ですよね。
JavaScriptで調べものをする際に、真っ先に見る方も多いでしょう。

そんなMDNですが読めていますか？

例えば[Arrayのページ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array)を見てみましょう。

---
## さらっと書かれているprototype
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/ec2cbc8e-80dd-cdf5-95ff-4123169a4874.png)

---
## JavaScriptを理解するにはプロトタイプチェーンを知る必要がある

---
## 本記事の目的
* プロトタイプチェーンを理解する
* MDNをより読めるようにする
* JavaScriptのObjectやArrayなどの基本型の構造を理解する

---
## 確認環境や記述について

* Chrome 52のコンソールで動作確認
* ES5で記述
* 説明のしやすさを重視、プロトタイプをあらわすのに`__proto__`を使って記述

---
## まずはオブジェクトについてみていく

---
## シンプルなオブジェクトを作る

```JavaScript:リテラルでオブジェクトを作る
var obj = {name: "taro"};
```

```JavaScript:new演算子でオブジェクトを作る
var obj = new Object();
obj.name = "taro";
```

上記2つの同じオブジェクトを作っています。
※`{}`は`new Object()`の糖衣構文

---
## console.logで中身を出力してみる

```javaScript
var obj = {name: "taro"};
console.log(obj);
```

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/94e83923-6ec7-3d7e-5170-f180cf21bbd2.png)

`Object {name: "taro"}`というのが出てきました。

---
## Objectを展開

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/e7458336-d284-435f-6cf4-38a13772e1b0.png)

**`__proto__`という未知のプロパティが出てきます。**
これはオブジェクトを作ると裏で作られるプロパティです。

---
## `__proto__`のObjectを展開
プロパティがたくさん設定されているオブジェクトの様です。

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/5d210814-a27f-3039-924b-406847021da3.png)

---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/22ef3c16-c57e-07ee-39a1-8cbd151466e2.png)

---
## 謎のオブジェクトの正体とは

---
## MDNで検索
出てきたFunction名で検索をかけると[Objectプロトタイプオブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object#Methods)に含まれるメソッドと一致します。

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/cc6212ba-0464-c455-6458-c9b3e870d2c8.png)

---
## 謎のオブジェクトの正体はObject.prototype

```JavaScript:確認
obj.__proto__ === Object.prototype; // true
```
上記コードで`true`になるのが確認できます。

---
## `Object`自体がオブジェクト
`Object.prototype`と言う書き方は`obj.name`のような書き方と全く同じです。
`Object`という変数名でオブジェクトが定義されており、Objectのプロパティにprototypeが設定されています。

```JavaScript:あくまでイメージです
var Object = {prototype: {オブジェクトのプロトタイプ}};
```
---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/231d326b-1766-a1d1-cbaf-245e11aa3f66.png)

---
## そもそも`Object`とは

---
## `Object`という変数は`window`に定義されている
この`Object`という変数はブラウザのグローバルオブジェクトである`window`に定義されています。

```JavaScript:確認
obj.__proto__ === window.Object.prototype; // true
```

---
## おまけ：`obj`という変数も`window`に定義されている
自分で作ったこの`obj`という変数もブラウザのグローバルオブジェクトである`window`に定義しています。

```JavaScript:確認
window.obj.__proto__ === window.Object.prototype; // true
```

---
## ここまでの図解

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/45d5b92b-2561-e4ce-4c99-7933c8eef529.png)

---
## いよいよプロトタイプチェーンに触れていく

---
[MDNのリファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object#Methods)を見るとObject.prototypeには`toString()`というメソッドがあることがわかる。
試しに呼んでみる。

```JavaScript
console.log(obj.name); // taro -> わかる

console.log(
  obj.__proto__.toString
); // function　toString() { [native code] } -> 実装はネイティブコードらしい
```

---
## これはどうなる？
```JavaScript
console.log(
  obj.toString
); // function　toString() { [native code] }  -> 呼べてしまった

console.log(
  obj.hogehoge
); // undefined -> 未定義？
```

---
## プロトタイプチェーンの仕組み

1. 指定したオブジェクトにプロパティが存在を調べる
1. なかった場合`__proto__`が参照する先で存在を調べる
1. それでもなかった場合`__proto__`が参照する・・・（ループ）
1. 最終的にnullになるまで行う。nullなら`undefined`を返す

---
## Objectのプロトタイプの`__proto__`はnull

```JavaScript
obj.__proto__.__proto__ === null; // true
Object.prototype.__proto__; // null
```

つまり`__proto__`を辿ってObject.prototypeで見つからなかったら`undefined`を返すということ。

※`obj.__proto__`と`Object.prototype`は同じものです（重要なので何度でも書く）

---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/73cc76fe-ce26-4c32-9070-5baf921525d4.png)

---
## プロトタイプチェーンの流れを見る
先程記述したそれぞれのパターンごとに動きを見てみます。

* obj.name （インスタンスに存在するパターン）
* obj.toString （プロトタイプチェーン先に存在するパターン）
* obj.hogehoge （最後まで見つからないパターン）

---
## `obj.name`を探す際の動き

---
## 1.`obj`を探す
![プロトタイプチェーン1_1.png](https://qiita-image-store.s3.amazonaws.com/0/35595/cfce308e-1c05-b36c-cc40-1dacee9b7b51.png)

---
## 2.`obj.name`を探す
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/75674c69-4218-545b-1b5f-faaa7d9d9a09.png)

---
## `obj.toString`を探す際の動き
---
## 1.`obj`を探す
![プロトタイプチェーン1_1.png](https://qiita-image-store.s3.amazonaws.com/0/35595/cfce308e-1c05-b36c-cc40-1dacee9b7b51.png)

---
## 2.`obj.toString`を探す
![プロトタイプチェーン2_2.png](https://qiita-image-store.s3.amazonaws.com/0/35595/d17be167-3f65-65d5-ef68-1c58f1bdf42b.png)

---
## 3.`obj.__proto__`を探す
![プロトタイプチェーン2_3.png](https://qiita-image-store.s3.amazonaws.com/0/35595/125aadb7-670c-1cfa-5a06-260b6a00b9cb.png)

---
## 4.`obj.__proto__.toString`を探す
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/25655df2-fb11-2d12-7db7-da459d0e6361.png)

---
## `obj.hogehoge`を探す際の動き
---
## 1.`obj`を探す
![プロトタイプチェーン1_1.png](https://qiita-image-store.s3.amazonaws.com/0/35595/cfce308e-1c05-b36c-cc40-1dacee9b7b51.png)

---
## 2.`obj.hogehoge`を探す
![プロトタイプチェーン3_2.png](https://qiita-image-store.s3.amazonaws.com/0/35595/44e27ea6-f1db-ceef-b9c2-cd1b9383b564.png)

---
## 3.`obj.__proto__`を探す
![プロトタイプチェーン2_3.png](https://qiita-image-store.s3.amazonaws.com/0/35595/125aadb7-670c-1cfa-5a06-260b6a00b9cb.png)

---
## 4.`obj.__proto__.hogehoge`を探す
![プロトタイプチェーン3_4.png](https://qiita-image-store.s3.amazonaws.com/0/35595/a770b8bb-aad3-105f-bbb7-5dc2968bedca.png)

---
## 5.`obj.__proto__.__proto__`を探す
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/0ac9b757-036a-2001-0094-9839068b41ee.png)

---
## Functionオブジェクトについて
さらにプロトタイプチェーンを深く知るにはFunctionオブジェクトについて知る必要があります。

※Objectでだいぶプロトタイプチェーンに強くなっているはずです。説明もささっといきます。

---
## シンプルなFunctionオブジェクトを作る

```JavaScript:リテラルでFunctionオブジェクトを作る
var func = function(){console.log("hello");};
```

```JavaScript:new演算子でFunctionオブジェクトを作る
var func = new Function('console.log("hello");');
```

上記2つのほぼ同じオブジェクトを作っています。(後者はfunc.nameが"anonymous"になったりする）
※`function(){}`は`new Function()`の糖衣構文

---
## Functionの`__proto__`
Objectのインスタンスの`__proto__`はObject.prototypeでしたね。

```JavaScript
(new Object()).__proto__ === Object.prototype; // true
```

この形はObject以外でも同じです。

```JavaScript
(new Function()).__proto__ === Function.prototype; // true
(new Array()).__proto__ === Array.prototype; // true
```
---
## つまり、FunctionもObjectと同じようなプロトタイプが構成されている

---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/ff162f4a-f0d8-eb6f-8636-ea2765ccc9ee.png)

---
## Functionのプロトタイプにも`__proto__`がある
プロトタイプチェーンは`__proto__`を辿るんでしたね。
みてみます。

```JavaScript:__proto__を辿る
console.log(func.__proto__.__proto__);
```

![image](https://qiita-image-store.s3.amazonaws.com/0/35595/847c6d0e-292b-db48-5491-8677ff6b2553.png)

---
## これObjectのときに出てきたやつだ！！

---
## FunctionもObjectのプロトタイプチェーンが組まれている

```JavaScript:確認
Function.prototype.__proto__ === Object.prototype; // true
```

Function.prototypeにtoStringが定義されているため（いわゆるオーバーライド）
toStringはFunction.prototype.toStringになります。

```JavaScript:確認
func.toString(); // function (){console.log("hello");}
func.toString === Function.prototype.toString; // true;
```
---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/c8bed334-b642-6f99-7004-7fe437cd155d.png)

---
## コンストラクタ関数
コンストラクタ関数は`new XXX()`に入る`XXX`関数のこと。
つまり`Object`や`Function`などはコンストラクタ関数として使用できるFunctionオブジェクト。

---
## constructorプロパティ
コンストラクタ関数を参照するためのプロパティ。
プロトタイプやインスタンスが持っている。

```JavaScript:今まで出てきた例のconstructor
var obj = {};
Object.prototype.constructor === Object; // true
obj.constructor === Object; // true

var func = function(){};
Function.prototype.constructor === Function; // true
func.constructor === Function; // true
```

---
## ここまでの図解
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/d67f5204-59e4-4b8f-b54f-4fcc8c455b2c.png)

---
## 最初に見た[ArrayのMDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array#Properties)にアクセスして見てみよう
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/ec2cbc8e-80dd-cdf5-95ff-4123169a4874.png)

---
## ある程度は読めるようになったはず
なってなかったら・・・筆者の説明スキル不足、申し訳ない :cry: 

本記事の内容が理解できていれば
[MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array#Properties)と以下のコードで次ページのようなイメージができるはずです。

```JavaScript:配列を生成
var myArray = [];
myArray.push("hoge"); // これはプロトタイプチェーン先のメソッドから呼べる
myArray.isArray; // 何が返るかわかりますね？
// myArray.isArray(); // undefined()を実行しているようなもの
Array.isArray(myArray); // true isArrayの使い方はこう

```

---
![image](https://qiita-image-store.s3.amazonaws.com/0/35595/5486d07d-8cc2-6459-ddd2-df4c898eec5c.png)

---
## あとがき
プロトタイプチェーンを理解できた人が一人でも多くいれば幸いです。

本記事の目的はプロトタイプチェーンを理解することです。
プロトタイプチェーンを使ってごりごり実装しようということではありません。
原則としては`class構文`使いましょう。（classも内部的にはプロトタイプチェーン）

---
## 参考資料

書籍
[開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質](https://www.amazon.co.jp/gp/product/487311621X)
[Effective JavaScript　JavaScriptを使うときに知っておきたい68の冴えたやり方](https://www.amazon.co.jp/gp/product/4798131113/)
[JavaScript Ninjaの極意](https://www.amazon.co.jp/JavaScript-Ninja-Programmers-SELECTION/dp/4798128457/)

記事
[[JavaScript] そんな継承はイヤだ - クラス定義 - オブジェクト作成](http://qiita.com/LightSpeedC/items/d307d809ecf2710bd957)
[や...やっと理解できた！JavaScriptのプロトタイプチェーン](http://maeharin.hatenablog.com/entry/20130215/javascript_prototype_chain)