JavaScriptにおけるオブジェクトのプロトタイプ

JavaScriptのオブジェクトは、別のオブジェクトと**プロトタイプ**と呼ばれる関係性を持つことができる。

## あるオブジェクトをプロトタイプとした新しいオブジェクトの作り方

あるオブジェクトをプロトタイプとした新しいオブジェクトを作るには[`Object.create()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create)を使う。

```js
const prototype = { a: 'apple' };
const obj = Object.create(prototype);
```

このとき、「`prototype`は`obj`のプロトタイプである」とか「`obj`は`prototype`を継承している」とか言う。

## プロトタイプの参照方法

オブジェクトのプロトタイプを参照する方法は2つある。

* `obj.__proto__`
* `Object.getPrototypeOf(obj)`

前者の`__proto__`は非推奨であり、後者の[`Object.getPrototypeOf()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf)を使うべきとされている。しかし前者は表記が簡潔なので、プロトタイプの説明ではよく使われる。

```js
const prototype = { a: 'apple' };
const obj = Object.create(prototype);

console.log(obj.__proto__ === prototype); // => true
```

## プロトタイプのプロパティの参照方法

プロトタイプのプロパティ（メソッドも含む）は、自身のプロパティのように参照できる。

```js
const prototype = { a: 'apple' };
const obj = Object.create(prototype);

console.log(obj.a); // => "apple"
```

## オブジェクトリテラルで作成したオブジェクトのプロトタイプ

[オブジェクトリテラル](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Object_literals)（`{ ... }`）でオブジェクトを作成すると、そのオブジェクトは[`Object.prototype`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype)を継承する。

```js
const obj = { a: 'apple' };

console.log(obj.__proto__ === Object.prototype); // => true
```

`Object.prototype`は[`.toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)などのメソッドを持つので、オブジェクトリテラルで作成したオブジェクトは、自身のメソッドのようにそれらを使うことができる。

```js
const obj = { a: 'apple' };

console.log(obj.toString()); // => "[object Object]"
```

## `new`演算子で作成したオブジェクトのプロトタイプ

例えば`const date = new Date();`とすると、`date`は[`Date.prototype`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/prototype)を継承する。

```js
const date = new Date();

console.log(date.__proto__ === Date.prototype); // => true
```

このように、[`new`演算子](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)で`new Foo()`として作成したオブジェクトは、普通は`Foo.prototype`を継承する。（`Foo`の作りによっては、そうではないこともあり得る。）

### 備考：クラス構文のメソッド定義

JavaScriptのクラス構文で定義したメソッドは、そのクラスの`.prototype`のメソッドになる。ただし、[staticキーワード](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static)で定義したメソッドは、そのクラスのメソッド（静的メソッド）になる。

```js
class Foo {
  constructor() {
  }
  method1() {
  }
  method2() {
  }
  static method3() {
  }
}

console.log(Object.getOwnPropertyNames(Foo.prototype)); // => ["constructor", "method1", "method2"]
console.log(Object.getOwnPropertyNames(Foo)); // => ["length", "prototype", "method3", "name"]
```

## 配列のプロトタイプ

JavaScriptでは、配列もオブジェクトである。

配列は[`Array.prototype`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype)を継承している。よって、配列は自身のメソッドのように`Array.prototype`のメソッドを使える。

```js
const arr = [1, 2, 3];

console.log(arr.__proto__ === Array.prototype); // => true
console.log(arr.join('-')); // => "1-2-3"
```

## プロトタイプチェーン

配列のプロトタイプは`Array.prototype`である。そして実は`Array.prototype`にもプロトタイプがあり、それは`Object.prototype`である。

```js
const arr = [1, 2, 3];

console.log(arr.__proto__ === Array.prototype); // => true
console.log(arr.__proto__.__proto__ === Object.prototype); // => true
```

このようなプロトタイプの連鎖のことを、**プロトタイプチェーン**と呼ぶ。

プロトタイプのプロパティは自身のプロパティのように参照できことは説明したが、プロトタイプのプロトタイプのプロパティも同様に、自身のプロパティのように参照できる。

例えば次のようにすると、`arr`のプロトタイプである`Array.prototype`のプロトタイプである`Object.prototype`の[`.valueOf()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)が呼ばれる。

```js
const arr = [1, 2, 3];

console.log(arr.valueOf());
```

あるオブジェクトのプロパティを参照しようとするとき、オブジェクトがそのプロパティを持っていればそれが返される。なければ、プロトタイプチェーンを辿っていき、プロパティが見つかった時点でそれが返される。

ちなみに、`Object.prototype`のプロトタイプは`null`である。

```js
console.log(Object.prototype.__proto__ === null); // => true
```

## プロトタイプチェーンの終端

プロパティを探すためにプロトタイプチェーンを辿っていき、`null`に行き着いたらそこで探索は終わり、`undefined`が返される。

例えば次のようにすると、`arr`も、`arr`のプロトタイプである`Array.prototype`も、`Array.prototype`のプロトタイプである`Object.prototype`も`hello`プロパティを持たず、`Object.prototype`のプロトタイプは`null`なので、`undefined`が返る。

```js
const arr = [1, 2, 3];

console.log(arr.hello); // => undefined
```