【JS学習その⑨】JavaScriptにおけるクラスとその継承

#JS学習シリーズの目的
このシリーズは、私ジャックが学んだJavaScriptのメカニズムについてアウトプットも兼ねて 
皆さんと知識や理解を共有するためのものです。
(理解に間違いがあればご指摘いただけると幸いです)

#クラスとは
「**コンストラクター関数をクラス表記で書けるようにしたもの**」
※このように、既にある機能を簡単に書けるようにしたものを**シンタックスシュガー**と表現します。
コンストラクター関数については➡[【JS学習その⑧】コンストラクター関数 ～prototype・new演算子・instanceof～](https://qiita.com/jack1-2-3/items/db55294f4c51fde243f1)

```main.js
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.hello = function() {
    console.log('hello ' + this.name);
}
```
上記のようなコンストラクター関数をクラスに書き換えると次のようになります。

```main.js
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    hello() {
        console.log('hello ' + this.name);
    }
}
```
上記の`constructor(){}`関数が、コンストラクターと同じ役割を果たします。
そして、`prototype`に格納しているメソッドは、`constructor() {}`と同じ並びに追加します。
このように、コンストラクター関数と`prototype`で定義するのと、クラスで定義するのは実質同じ意味になります。
クラスの内部で動いているのは`prototype`を使った仕組みになるので、クラスはコンストラクター関数の**シンタックスシュガー**ということになります。

```main.js
const bob = new Person('Bob', 23);
console.log(bob);
```
インスタンスを生成する際の呼び出し方も同じになります。

#クラス継承とは
「**他のクラスのプロパティーとメソッドを継承すること**」

まずコンストラクター関数で継承する場合を見てみましょう]
前述した`Person`から新たに`Japanese`に継承してみます

```main.js
//継承元となるPerson
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.hello = function() {
    console.log('hello ' + this.name);
}

// 継承先となるJapanese
function Japanese(name, age, gender) {
    Person.call(this, name, age); /*Personのプロパティを継承*/
    this.gender = gender; /*Japanese独自のプロパティー*/
}

Japanese.prototype = Object.create(Person.prototype); /*Personのprototypeを継承*/

// 継承先のJapanese内で独自のメソッドを作成
Japanese.prototype.hello = function() {
    console.log('Konnichiwa ' + this.name);
}

Japanese.prototype.bye = function() {
    console.log('Sayonara ' + this.name);
}

const taro = new Japanese('Taro', 23, 'Male');
```
次にクラスで継承してみます

```main.js
// 継承元となるPersonクラス
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    hello() {
        console.log('hello ' + this.name);
    }
}

// 継承先となるJapaneseクラス
class Japanese extends Person {
    constructor(name, age, gender) {
        super(neme, age); /*Personクラスのプロパティーを継承*/
        this.gender = gender;
    }

    //Japaneseクラス独自のメソッド
    hello() {
        console.log('Konnichiwa ' + this.name);
    }

    bye() {
        console.log('Sayonara ' + this.name);
    }
}

const taro = new Japanese('Taro', 23, 'Male');
taro.bye(); /*Sayonara Taro*/
```
上記のコードのように、クラス継承では、新たに継承先クラスを作る際に`extends`というキーワードで継承元のクラスを指定します。
そして、`constructor() {}`関数内で`super`というキーワードを使い、引数に継承元の`constructor`関数に使用している引数を指定することで、継承元クラスのプロパティーを継承することができます。
注意点としては、継承先クラス(今回の場合はJapanese)で新たに`this`を使ってプロパティーを指定する場合、**先に`super`で継承元のプロパティーを継承してから継承先クラス独自のプロパティーを設定してください**。(でないとエラーとなります)

##superとは
「**継承元の関数を呼び出すためのキーワード**」

前述したように、`super`は継承元のプロパティーを継承先に継承できますが、メソッドも継承できます。

```main.js
// 継承元となるPersonクラス
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    hello() {
        console.log('hello ' + this.name);
    }
}

// 継承先となるJapaneseクラス
class Japanese extends Person {
    constructor(name, age, gender) {
        super(neme, age); /*Personクラスのプロパティーを継承*/
        this.gender = gender;
    }

    //Japaneseクラス独自のメソッド
    hello() {
        super.hello(); /*Personのhelloメソッドを継承*/
        console.log('Konnichiwa ' + this.name);
    }

    bye() {
        console.log('Sayonara ' + this.name);
    }
}

const taro = new Japanese('Taro', 23, 'Male');
taro.hello(); /*hello Taro*/
              /*Konnichiwa Taro*/
```
基本的に`super`はクラス内で使用しますが、一部例外的にオブジェクトの中でも呼べます。

```main.js
const american = {
    hello() {
        console.log('hello ' + this.name);
    }
};

const bob = {
    name: 'Bob',
    hello() {
        super.hello(); /*americanオブジェクトのhelloメソッドを継承*/
        console.log('konnichiwa ' + this.name);
    }
};

Object.setPrototypeOf(bob, american); /*bobオブジェクトのprototypeにamericanオブジェクトを設定*/
bob.hello(); /*hello Bob*/
             /*konnichiwa Bob*/

// 文法エラーとなる書き方
bob.bye = function() {
    super.hello();
};
```
上記のコードのように、
`american`オブジェクトを`bob`オブジェクトの`prototype`に`Object.setPrototypeOf`で設定します。
これで`american`が`bob`に継承されたので、`bob`オブジェクトの`hello`メソッド内で`super`を使って`american`オブジェクトの`hello`メソッドを継承します。
そして、`bob.hello()`でメソッドを実行するとコメントの内容になります。
ただし、これは**オブジェクトリテラルの中でしか`super`は使えません**。
よって、上記のコードの最後に書いているように新たにプロパティーを設けてその中で`super`を使って継承はできません。(SyntaxError(文法エラー)になります)

#まとめ
いかがでしたでしょうか。
クラスも、コンストラクター関数を理解した後だとさらに理解が深まり、その便利さがよく分かるのではないでしょうか。
今や、クラスはJavaScriptにおいてなくてはならない技術なので、しっかり理解しておきましょう！