JavaScript の prototype の復習

[ドワンゴ Advent Calendar](http://qiita.com/advent-calendar/2016/dwango) の 5 日目の記事です。ドワンゴといえばカドカワ。カドカワといえば角川ゲームスの RPG ツクール MV。 RPG ツクール MV といえば JavaScript なので、今回は JavaScript に関する記事です。

RPG ツクール MV をいじって遊んでいたら、 prototype について案外勘違いしてしまっていたところがあったので、復習を兼ねて JavaScript の prototype についてメモします。ついでに RPG ツクール MV で使用されている継承イディオムについてもメモしています。なお、 ES6 以降では無論こういったイディオムを使わずに `class` を使うべきですが、その場合でも裏方の仕組みである prototype の仕組みについて知っておいて損はないでしょう。

# `__proto__` と `prototype`

* オブジェクトには `[[Prototype]]` というプロトタイプチェーンに使われる内部スロットがある ([9.1](http://www.ecma-international.org/ecma-262/7.0/#sec-ordinary-object-internal-methods-and-internal-slots))。
  * プロトタイプチェーンは、オブジェクトのプロパティを探索する仕組みである (本稿では説明を割愛する)。
* `__proto__` は内部メソッド `[[GetPrototypeOf]]` および `[[SetPrototypeOf]]` を通じて `[[Prototype]]` を暴露するプロパティである ([9.1](http://www.ecma-international.org/ecma-262/7.0/#sec-ordinary-object-internal-methods-and-internal-slots), [B.2.2.1](http://www.ecma-international.org/ecma-262/7.0/#sec-object.prototype.__proto__))。
* `__proto__` は ES6 で標準化されたものの、表立って使用することは望ましくない。 `__proto__` に何かを突っ込みたい場合は、以下に説明する `new` 演算子や `Object.create` を使用することが望ましい。
* `new` 演算子のオブジェクト生成時に `__proto__` にセットされる値には、コンストラクタ関数の `prototype` が使用される ([6.1.7.2](http://www.ecma-international.org/ecma-262/7.0/#sec-object-internal-methods-and-internal-slots), [12.3.3](http://www.ecma-international.org/ecma-262/7.0/#sec-new-operator))。

# `new` 演算子

```js
function Foo() {}
var foo = new Foo();
```

は基本的に以下と等価。

```js
function Foo() {}
var foo = {};
foo.__proto__ = Foo.prototype; // プロトタイプオブジェクトの設定
Foo.apply(foo);                // foo を this として Foo 関数を呼ぶ
```

# `constructor` プロパティ

```js
function Foo() {}
var foo = new Foo();

// foo.constructor という呼び出しは foo.__proto__.constructor の呼び出しになる。
// foo 自身は constructor というプロパティを持っていない。
console.log(foo.hasOwnProperty('constructor')); // false
console.log(foo.__proto__.hasOwnProperty('constructor')); // true
console.log(Foo.prototype.hasOwnProperty('constructor')); // true

// 以下はすべて等価
console.log(foo.constructor === Foo) // true
console.log(foo.__proto__.constructor === Foo) // true
console.log(Foo.prototype.constructor === Foo) // true
```

# `Object.create` 関数

`Object.create` は、引数をプロトタイプ (`__proto__`) に持つオブジェクトを生成する ([4.3.5](http://www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-prototype), [19.1.2.2](http://www.ecma-international.org/ecma-262/7.0/#sec-object.create))。

```js
function Foo {};
var foo = Object.create(Foo.prototype);
```

は基本的に以下と等価。

```js
function Foo {};
var foo = {};
foo.__proto__ = Foo.prototype;
```

誤解を恐れずに言うと、「コンストラクタを実行しない `new`」である。

# 継承

## 古いやり方

```js
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
```

一見よさそうに見えるが、これには問題がある。  `Bar` のインスタンスごとに `Foo` のインスタンス (`Foo.prototype` を `__proto__` に持つオブジェクト) がほしいが、この継承の時点ではコンストラクタ `Foo` を実行してほしくない。そのために `Object.create` を使う。

## `Object.create` を使うやり方

```js
function Foo() {}
function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
// この時点だと Bar.prototype.constuctor は Foo なので、 Bar に直す必要がある。
Bar.prototype.constructor = Bar;
```

サイの表紙で有名な「サイ本」やいま巷で話題の RPG ツクール MV はこの方式。 `Bar` を以下のようにすることで親クラスコンストラクタを呼ぶことができる。

```js
function Bar() {
  Foo.apply(this, arguments);
}
```

# 参考

* [ECMAScript® 2016 Language Specification](http://www.ecma-international.org/ecma-262/7.0/)
* [Object.create() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
* [プログラマのためのJavaScript (7)：プロトタイプ継承の正体 - 檜山正幸のキマイラ飼育記](http://d.hatena.ne.jp/m-hiyama/20050908/1126154117)