Javascriptで継承

を実現するにはどうすれば良いのか、ということについてです💕。

# オブジェクト

継承の前にオブジェクトの作り方を見ましょう。オブジェクトを作成するには、オブジェクトを作成するための関数であるコンストラクタを定義しなければなりません（他に、オブジェクトをリテラルとして記述する記法もありますが）。コンストラクタは次のように作成することができます（以下のコードの記述と実行はNode.jsのREPLを使っています）。

```js
> function Currency(name, code) {
... this.name = name;
... this.code = code;
... this.toString = function() {
..... return this.name + " " + this.code;
..... }
... }
undefined
```

普通の関数と同じ書き方ですが、`this`を使うことによって、この関数が`new`を使ってコンストラクタとして呼び出された際に、実体化されるオブジェクトにプロパティが設定されるようにしています。つまり、`this`を使うことによって、実体化されたオブジェクトが持つことになるプロパティを定義することができます。

このコンストラクタを使ってオブジェクトを実体化するには、次のように`new`を使ってコンストラクタを呼び出します。オブジェクトのプロパティが正しく呼び出せるかも確認しましょう。

```js
> let yen = new Currency("yen", "JPY")
undefined
> yen
Currency { name: 'yen', code: 'JPY', toString: [Function] }
> yen.name
'yen'
> yen.code
'JPY'
> yen.toString()
'yen JPY'
```

# プロトタイプ

JavaScriptのオブジェクトにはプロトタイプという面白い仕組みが備わっています。これは、オブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されている別のオブジェクトのプロパティを、あたかもオブジェクト自体のプロパティであるかのように呼び出すことができるという機能です。少々分かり辛いのでコードで見ましょう。

上のコードからの続きで、まずコンストラクタの`prototype`プロパティの内容を見てみましょう（JavaScriptでは関数もオブジェクトの一種なので関数もプロパティを持つことができます。そして、全ての関数は`prototype`プロパティを最初から持っています）。

```js
> Currency.prototype
Currency {}
```

どうやら空のオブジェクトのようですね。コンストラクタの`prototype`プロパティは最初は空のオブジェクトです。

（実は、厳密には空ではありません。`constructor`プロパティだけは最初から設定されています。`constructor`プロパティは文字通りコンストラクタの参照を保持します。

```js
> Currency.prototype.constructor
[Function: Currency]
```

）

この状態で`yen`オブジェクトの`mai`プロパティ、すなわち、`yen.mai`を参照すると何が得られるでしょうか。

```js
> yen.mai
undefined
```

`yen`オブジェクトに`mai`プロパティは存在しませんので`undefined`が返ってきます。

では、`Currency`コンストラクタの`prototype`プロパティに最初から設定されているオブジェクトに新しいプロパティを追加してみましょう。

```js
> Currency.prototype.mai = 10000
10000
> Currency.prototype.hon = 1000000
1000000
> Currency.prototype
Currency { mai: 10000, hon: 1000000 }
```

（どうでも良いですが、為替の世界では1万通貨を1枚と言ったり、100万通貨を1本と言ったりします）。

この状態で再び`yen.mai`を参照すると何が得られるでしょうか。

```js
> yen.mai
10000
```

`yen`オブジェクトに`mai`プロパティは存在しませんが、`yen`オブジェクトを実体化した`Currency`コンストラクタの`prototype`プロパティに設定されているオブジェクトには`mai`プロパティが存在しますので、このプロパティの値が返ってきます。

これがプロトタイプという機能です。

ここで返ってきている値はオブジェクト自体が持っているものではないことに注意してください。そうではなく、同じコンストラクタ（この場合は`Currency`コンストラクタ）で実体化された全てのオブジェクト間で共有されることになる値です（更に継承が入ってくると、コンストラクタの子コンストラクタで実体化されたオブジェクトとの間でも共有される可能性があります）。値自体は、コンストラクタの`prototype`プロパティに格納されているオブジェクトが持っているものです。

```js
> let doru = new Currency("doru", "USD")
undefined
> doru.mai
10000
> Currency.prototype.mai = 1000
1000
> yen.mai
1000
> doru.mai
1000
```

そのため、`prototype`プロパティの内容を変えれば、それはオブジェクトの方にも反映されることになります。

では、逆に、オブジェクト自体に`mai`プロパティが存在する（オブジェクト自体が`mai`プロパティを持っている）場合はどうなるでしょうか。`yen.mai`に値を設定してみましょう。

```js
> yen.mai = 100
100
> yen.mai
100
> Currency.prototype.mai
1000
> doru.mai
1000
```

`yen.mai`に値を設定すると、`yen`オブジェクト自体の`mai`プロパティに値が設定され、`yen.mai`を参照するとその値が返ってきます。結果として`Currency.prototype.mai`は`yen`オブジェクトからは見えなくなります。しかし、依然として`Currency.prototype.mai`には元の値が設定されていますし、他のオブジェクト（ここでは`doru`オブジェクト）からは参照できます。

`delete`演算子で`yen`オブジェクト自体の`mai`プロパティを削除すれば元の状態に戻ります。

```js
> delete yen.mai
true
> yen.mai
1000
```

さて、ここまでは`prototype`プロパティに最初から設定されているオブジェクトに新しいプロパティを追加していましたが、`prototype`プロパティ自体に別のオブジェクトを設定することもできます。

```js
> Currency.prototype = {}
{}
> Currency.prototype.constructor
[Function: Object]
> Currency.prototype.constructor = Currency
[Function: Currency]
> Currency.prototype.constructor
[Function: Currency]
```

`Currency`コンストラクタの`prototype`プロパティに空のオブジェクトを設定してみました。ただ、これでは`constructor`プロパティが正しくなくなってしまいますので、`constructor`プロパティを自分で設定し直しました。

この状態で`yen.mai`や`doru.mai`を参照すると何が得られるでしょうか。

```js
> yen.mai
1000
> doru.mai
1000
> Currency.prototype.mai
undefined
```

`Currency.prototype.mai`は`undefined`であるにも拘らず、`yen.mai`や`doru.mai`は`1000`です。これは、`yen`オブジェクトや`doru`オブジェクトが古い`prototype`プロパティの内容を参照しているからです。

この状態で`Currency`コンストラクタを使って新しいオブジェクトを作成した場合、そのオブジェクトは新しい`prototype`プロパティの内容を参照します。

```js
> var yuuro = new Currency("yuuro", "EUR")
undefined
> yuuro.mai
undefined
```

このように、コンストラクタの`prototype`プロパティ自体に別のオブジェクトを設定した場合は、それ以前にそのコンストラクタを使って作成したオブジェクトの参照まで置き換わる訳ではないので注意してください。

# プロトタイプ鎖

オブジェクトのプロパティを取得しようとした際にオブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されているオブジェクトのプロパティも調べるというプロトタイプの動作は連鎖します。つまり、オブジェクトのプロパティを取得しようとしたときには、JavaScriptのエンジンは次のような流れで値を返そうとします。

* オブジェクト自体にプロパティが存在しないか調べ、存在する場合にはそのプロパティの値を返す。
* 存在しない場合にはオブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されているオブジェクトにプロパティが存在しないか調べ、存在する場合にはそのプロパティの値を返す。
* 存在しない場合にはオブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されているオブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されているオブジェクトにプロパティが存在しないか調べ、存在する場合にはそのプロパティの値を返す。
* ・・・
* ただし、`Object.prototype`に到達しても値が得られなかった場合には、`undefined`を返す。

この連鎖は継承を実現するのに使えます。オブジェクトを実体化したコンストラクタの`prototype`プロパティに格納されているオブジェクトを親と見做せば、プロパティの取得の動作は、プロパティがオブジェクト自体に定義されていない場合には親のプロパティを見に行く動作（親にもプロパティが定義されていなければその親・・・と続けていく動作）となるからです。

コードで見ましょう。

```js
> function Currency(name, code) {
... this.name = name;
... this.code = code;
... }
undefined
> Currency.prototype.toString = function() {
... return this.name + " " + this.code;
... }
[Function]
> let yen = new Currency("yen", "JPY")
undefined
> yen.name
'yen'
> yen.code
'JPY'
> yen.toString()
'yen JPY'
```

ここまでは、`toString`関数を`Currency.prototype`に移動した点以外は最初のコードと同じです。ここから`CryptoCurrency`コンストラクタを`Currency`コンストラクタの子として（すなわち、`Currency`コンストラクタを継承するものとして）定義します。

```js
> function CryptoCurrency(name, code, author) {
... this.name = name;
... this.code = code;
... this.author = author;
... }
undefined
> CryptoCurrency.prototype = new Currency()
Currency { name: undefined, code: undefined }
> CryptoCurrency.prototype.constructor = CryptoCurrency
[Function: CryptoCurrency]
> CryptoCurrency.prototype.base = Currency.prototype
Currency { toString: [Function] }
> CryptoCurrency.prototype.toString = function() {
... return this.base.toString.call(this) + " " + this.author;
... }
[Function]
> let bitcoin = new CryptoCurrency("bitcoin", "BTC", "Satoshi Nakamoto")
undefined
> bitcoin.name
'bitcoin'
> bitcoin.code
'BTC'
> bitcoin.author
'Satoshi Nakamoto'
> bitcoin.toString()
'bitcoin BTC Satoshi Nakamoto'
```

`CryptoCurrency.prototype`には`new Currency()`を指定します。これだけでは`constructor`プロパティがおかしいので自分で設定します。それから`base`プロパティ（親のプロトタイプを参照するためのプロパティ）も定義しておくと便利かもしれません。

このようにして継承を実現するパターンを**疑似クラス的継承パターン**（pseudo-classical inheritance pattern）と言います。これはコンストラクタを疑似的なクラスとして利用しているからです。JavaScriptで継承を実現するパターンは他にも幾つかありますが、このパターンが最も基本的なものではないかと思います。

# おまけ

最後に、幾つか便利な演算子や関数を紹介しておきましょう。

ひとつめ。`instanceof`演算子であるオブジェクトがあるコンストラクタの実体であるか調べることができます。親のコンストラクタに対しても`true`となります。

```js
> yen instanceof Currency
true
> yen instanceof CryptoCurrency
false
> bitcoin instanceof Currency
true
> bitcoin instanceof CryptoCurrency
true
```

ふたつめ。`hasOwnProperty`関数であるプロパティがオブジェクト自体が定義しているものであるかを調べることができます。

```js
> yen.hasOwnProperty("name")
true
> yen.hasOwnProperty("code")
true
> yen.hasOwnProperty("toString")
false
> bitcoin.hasOwnProperty("name")
true
> bitcoin.hasOwnProperty("code")
true
> bitcoin.hasOwnProperty("author")
true
> bitcoin.hasOwnProperty("toString")
false
```

みっつめ。`isPrototypeOf`関数であるオブジェクトが別のオブジェクトを実体化したコンストラクタの`prototype`プロパティに設定されているものであるかを調べることができます。親のコンストラクタの`prototype`プロパティに設定されているものに対しても`true`となります。

```js
> Currency.prototype.isPrototypeOf(yen)
true
> CryptoCurrency.prototype.isPrototypeOf(yen)
false
> Currency.prototype.isPrototypeOf(bitcoin)
true
> CryptoCurrency.prototype.isPrototypeOf(bitcoin)
true
```