JavaScriptのプロトタイプチェーンを深堀りする

## プロトタイプチェーンとは
オブジェクトが継承元プロトタイプを参照し、そのプロトタイプがまたその継承元プロトタイプを参照し.....のような連鎖及び、その一連のオブジェクト群のことを**プロタイプチェーン**といいます。

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

>[図で理解するJavaScriptのプロトタイプチェーン - Qiita](https://qiita.com/howdy39/items/35729490b024ca295d6c)より引用

プロトタイプチェーンを理解するためには、**オブジェクト**と**オブジェクトプロトタイプ**を完全に別ものとして考えることが大事です。


## 配列のプロトタイプチェーン

[図で理解するJavaScriptのプロトタイプチェーン - Qiita](https://qiita.com/howdy39/items/35729490b024ca295d6c)で説明されていた配列のプロトタイプチェーンについて補足・解説します。

```JavaScript:配列を生成（記事より引用）
① var myArray = [];
② myArray.push("hoge"); // これはプロトタイプチェーン先のメソッドから呼べる
③ myArray.isArray(); // undefined()を実行しているようなもの
④ Array.isArray(myArray); // true isArrayの使い方はこう
```
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3332313333382f35323234656134352d323930302d323234372d323739632d3833373939306661376161652e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/2ac5dbe2-5059-e4ea-c0ad-711bb24d1d5e.png)


### ① 配列の定義
```JavaScript:空の配列を定義
① var myArray = [];
```
これは`myArray`配列を定義、つまり`myArrayインスタンス`を生成している以下の記述と同義です。

```JavaScript:Arrayオブジェクトのインスタンス化
var myArray = new Array();
```

それと同時に、コンストラクタ関数である`Arrayオブジェクト`の`prototype`が、`myArray.__proto__`に代入されます。

>\_\_proto__は全てのオブジェクトが持つ、内部プロパティ。そのオブジェクト自体のprototypeへの参照を持つ。

>[なるほど。。\_\_proto__とprototypeの違い | 武骨日記](https://kenjimorita.jp/__proto__vsprototype/)より引用

```JavaScript:同時に起こる
myArray.__proto__ = Array.prototype;   // Arrayオブジェクトのプロトタイプを代入
```
<img width="490" alt="スクリーンショット 2020-02-28 20.00.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/4f335d21-f82c-90c7-973f-3a431c3a5a78.png">


### ② pushメソッドはどこにある？

```JavaScript:配列に要素を追加するpushメソッド
② myArray.push("hoge");
```
myArrayオブジェクト自体には`pushメソッド`は存在しません。
オブジェクトにプロパティが存在しない場合、**プロトタイプチェーン**の仕組みにより、そのオブジェクトの`__proto__`が参照する先でプロパティの存在有無を確認します。

①より`myArray.__proto__ === Array.prototype;`なので、`myArray`は`Array.prototype`が持つ`pushメソッド`を参照することができます。

```JavaScript:myArrayがpushメソッドを使える理由
myArray.push("hoge");  // myArrayオブジェクトにはpushメソッドは存在しない。

↓  // myArray.__proto__を参照する。

myArray.__proto__ === Array.prototype  // true

↓  // Array.prototypeにpushメソッドがないか確認する。

Array.prototype.push  // Array.prototypeにはpushメソッドが存在するので、myArrayはpushメソッドを使える。
```
<img width="489" alt="スクリーンショット 2020-02-28 19.55.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/c46dfd48-52f8-5b85-476b-31ebdd1957a0.png">


### ③ myArray.isArray()はなぜエラーになる？

```JavaScript:なぜundefined？
③ myArray.isArray(); // undefined()を実行しているようなもの
```
`isArray()`とは、コンストラクタ関数`Array`が持つメソッドの一つです。
`myArray`はそのプロトタイプを含めて`isArray()`を所持していないので、③式は`undefined`になります。

```JavaScript:myArrayインスタンスはisArrayメソッドを参照できない
myArray.isArray();  // myArrayオブジェクトにはisArray()は存在しない。

↓  // myArray.__protoを参照する。

myArray.__proto__ === Array.prototype  // Array.prototypeにもisArray()は存在しない。

↓  // Array.prototype.__protoを参照する。

Array.prototype.__proto__ === Object.prototype  // Object.prototypeにもisArray()は存在しない。

↓  // Object.prototype.__proto__を参照する。

Object.prototype.__proto__ === null  // Object.prototype.__proto__はnullなので③式はundefinedとなる。
```
ほぼ全てのオブジェクトは`Object.prototype`を始祖に持ち[^1]、`Object.prototype`が**プロトタイプチェーンの終端**となります。
<img width="1034" alt="スクリーンショット 2020-02-28 20.04.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/9f227fd5-1dc5-9076-7ea1-3ffe4caeec6d.png">


[^1]: 例えば`Object.create(null)`で作られたオブジェクトはプロトタイプを持たないので、`Object.prototype`から継承を受けていない。

### ④ isArray()を正しく使うには？
インスタンスは`constructorプロパティ`を持っています。
そしてそのconstructorプロパティは、インスタンス化の元となったオブジェクト（継承元）のコンストラクタ関数を参照します。

つまり、`myArray.constructor`はその元となったオブジェクトである`Array`を参照しています。

```JavaScript:myArrayのconstructorプロパティはArrayオブジェクトを参照する
myArray.constructor === Array;  // true
```

Arrayオブジェクトは`isArrayメソッド`を所持しているので、isArrayの正しい使い方は以下になります。

```JavaScript:isArray()を正しく使うパターン
④ Array.isArray(myArray);  // true

myArray.constructor.isArray(myArray);  // true

Array.prototype.constructor.isArray(myArray);  // true
```
<img width="554" alt="スクリーンショット 2020-02-28 19.41.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/f8e9af05-4c27-4181-f260-785dfa903731.png">

以上が配列を定義した際のプロトタイプチェーン解説です。

## Objectオブジェクトのプロトタイプチェーン
すべてのオブジェクトは`__proto__プロパティ`を持つので、それは`Objectオブジェクト`も同様です。
プロトタイプチェーンの末端である`Object.prototype`を参照するのかと思いきや、以下記述は**false**になります。

```JavaScript:Object.__proto__の参照元はObject.prototypeではない
Object.__proto__ === Object.prototype  // false
```

それでは`Object.__proto__`は何を参照しているのでしょうか？

答えは`Function.prototype`です。

```JavaScript:Object.__proto__の参照元はFunction.prototype
Object.__proto__ === Function.prototype  // true
```

`Function.prototype`は、`Object.prototype`を参照することでプロトタイプチェーンの末端にたどり着きます。

```JavaScript:Objectオブジェクトのプロトタイプチェーン
Object.__proto__ === Function.prototype  // true
↓
Function.prototype.__proto__ === Object.prototype;  // true
↓
Object.prototype.__proto__ === null  // true
```
<img width="600" alt="スクリーンショット 2020-02-28 20.20.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/c8cc4e52-3a09-9381-bb8a-b10e10f6da01.png">


## Functionオブジェクトのプロトタイプチェーン
`Functionオブジェクト`の`__proto__`も`Function.prototype`を参照し、Objectオブジェクトと同様のチェーンを辿ります。


```JavaScript:Functionオブジェクトのプロトタイプチェーン
Function.__proto__ === Function.prototype  // true
↓
Function.prototype.__proto__ === Object.prototype;  // true
↓
Object.prototype.__proto__ === null  // true
```
<img width="624" alt="スクリーンショット 2020-02-28 20.25.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/be1101b1-da8b-53a2-21c7-420b8064fee9.png">


## toStringメソッドのややこしい話

Objectオブジェクトの持つメソッドのひとつに、`toString()`があります。
[Object.prototype.toString() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)

以下の例は同じ`toStringメソッド`を参照しているように見えますが、結果が異なります。

```JavaScript:toStringメソッドの比較
Object.toString === Object.__proto__.toString  // true
Object.toString === Object.prototype.toString  // false
```
<br>

理由を調べるため、`Object.toString`のプロトタイプチェーンを辿っていくと、
その参照先は`Function.prototype`を指していることが分かりました。

```JavaScript:Object.toStringのプロトタイプチェーン
Object.toString  // ObjectオブジェクトにはtoStringメソッドは存在しない

↓  // Object.__proto__を参照する

Object.__proto__ === Function.prototype  // true

↓  // Function.prototypeにtoStringメソッドがないか確認する。

Function.prototype.toString  // Function.prototypeにはtoStringメソッドが存在するので、ObjectはtoStringメソッドを使える。

↓  // Function.prototype.__proto__も見てみる。

Object.prototype.toString  // 親（prototype）と子で同名のメソッドを持つ場合、親メソッドは子メソッドでオーバーライド（上書き）される。
```
`Object.prototype`が持つ`toStringメソッド`と、`Function.prototype`が持つ`toStringメソッド`ではメソッドの内容が異なります。
`Objectオブジェクト`はその両者とも継承していますが、メソッドの**オーバーライド**により、親（`Object.prototype`）のメソッドが子（`Function.prototype`）のメソッドにより上書かれています。

<img width="623" alt="スクリーンショット 2020-02-28 22.05.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321338/50005a5e-871c-671b-0c88-b7ef57bfe39f.png">

よって持っている`toStringメソッド`の起源が異なることが、最初の例の真相です。

```JavaScript:toStringメソッドの書き換え
Object.toString === Object.__proto__.toString  // true
↓  // 書き換えると
Function.prototype.toString === Function.prototype.toString  // true


Object.toString === Object.prototype.toString  // false
↓  // 書き換えると
Function.prototype.toString === Object.prototype.toString  // false 
```
[Object.prototype.toString()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)と[Function.prototype.toString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString)は別のメソッドなんですね。

## 参考
[図で理解するJavaScriptのプロトタイプチェーン - Qiita](https://qiita.com/howdy39/items/35729490b024ca295d6c)
[なるほど。。\_\_proto__とprototypeの違い | 武骨日記](https://kenjimorita.jp/__proto__vsprototype/)
[継承とプロトタイプチェーン - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)
[Object - JavaScript | MDN]
(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object)
[Object.prototype - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype)
[Object.prototype.\_\_proto__ - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)
[Object.prototype.toString() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)
[Object.create() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
[Function - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function)
[Function.prototype.toString() - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString)