JavaScriptのクラスとインスタンス

# はじめに
JavaScriptのクラスとインスタンスの基本について学んだので備忘録としてまとめたいと思います。クラスとインスタンスの知識はReactを扱う際にも必要な知識なので改めてES6から実装された新機能は学ぶべきだと思いました。

## オブジェクトとは
クラスとインスタンスに入る前に**オブジェクト**についておさらいです。
オブジェクトは複数の値を**プロパティ**という名前をつけて管理できる格納庫みたいなものです。

```php
const user = {name: 'アナゴ', sex: 'male'};

# 定数名.プロパティ名で値を取得できます。
console.log(user.name);
```

またオブジェクトの値には**関数**を用いることができます。
また、その関数を呼び出すには`定数名.プロパティ名()`と記述します。
以下のコードでは関数の部分をアロー関数で記述しています。

```php:
const user = {
  name: 'アナゴ',
  greet: () => {
    console.log('おはよう');
  }
}
# 関数の呼び出し
user.greet();
```

## オブジェクトを量産したい
Webサービスでは上記のようなオブジェクトをいくつも扱っています。
例えば以下のようにオブジェクトをいくつも作成する場合は毎回ゼロから作成していたらとても手間になります。

そこで効率よくオブジェクトを作成するために設計図を用意し、ぞの設計図を元にデータを作成していきます。
この設計図のことを`クラス`、一方一つ一つのデータを`インスタンス`と呼びます。

## クラスの定義
まずは設計図であるクラスを定義します。
定義の仕方は`class クラス名`です。
ちなみにクラスの命名規則としては**キャメルケース(単語の先頭を大文字にする)**が慣例です。

```php:
class IsonoFamily {

}
```

## インスタンスの生成
設計図は用意できたので個別のデータを作成します。クラスからオブジェクトを生成には`new クラス名()`と記述します。このクラスから生成したオブジェクトを**インスタンス**といいます。

```php:
class IsonoFamily {
}

// IsonoFamilyクラスのインスタンスを定数namiheiに代入
const namihei = new IsonoFamily();
```
namiheiというインスタンスはこれで作成できました。しかしクラスの中身に何も記述していないのでインスタンスもカラの状態です。次にクラスに中身を追加していきます。

## コンストラクタ
クラスには**コンストラクタ**という機能が用意されています。
コンストラクタはインスタンスを生成するときに実行したい処理や設定を追加するための機能です。
クラスの中に`constructor() { }`と記述し、さらにこのコンストラクタの中に処理を記述します。

```php:
class IsonoFamily {
  // コンストラクタを追加
  constructor() {
    console.log('インスタンスを生成しました');
  }
  
}

const namihei = new IsonoFamily();
```

## プロパティと値を追加
コンストラクタの中で生成したインスタンスに情報を追加します。
`this.プロパティ名 = 値`と記述することで情報を追加できます。

```php:
class IsonoFamily {
  constructor() {
    
    // ageプロパティを追加
    this.age = 54;
  }
}

const namihei = new IsonoFamily();

// 年齢を出力
console.log(namihei.age);
```

## インスタンスごとに値を変える
現段階のままでは新しくインスタンスを生成してもどれも同じ情報が付与されてしまいます。
これを解決するには`constructor(age, voiceActor)`のようにコンストラクタに引数を渡してあげます。

またインスタンスを生成する際に`new クラス名()`の`()`に値を追加します。

```php:
class IsonoFamily {
  // 引数に「name」と「age」を追加してください
  constructor(voiceActor, age) {
    // voiceActorを引数として受け取る
    this.voiceActor = voiceActor;
    
    // ageを引数として受け取る
    this.age = age;
  }
}

// 引数に「"モカ"」と「8」を渡してください
const namihei = new IsonoFamily("茶風林", 54);

console.log(`声優: ${namihei.voiceActor}`);
console.log(`年齢: ${namihei.age}`);
```

## メソッドとは
メソッドとはインスタンスの動作みたいなものです。
メソッドはクラスの中で`メソッド名(){ }`と記述し定義します。関数とにたようなものなので`{ }`内に処理を記述していきます。またメソッドを呼び出す際は`インスタンス.メソッド名()`と記述します。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  // greetメソッドを追加
  greet() {
    console.log('ばかもん！');
  }
  
}

const namihei = new IsonoFamily("茶風林", 54);

console.log(`声優: ${namihei.voiceActor}`);
console.log(`年齢: ${namihei.age}`);

// namiheiに対してgreetメソッドを呼びだす
namihei.greet();
```

## メソッド内で値を使う
メソッド内でインスタンスの値を使うには`this.プロパティ名`と記述します。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  greet() {
    console.log('ばかもん！');
  }
  
   // infoメソッドを追加
  info() {
    console.log(`声優は${this.voiceActor}です`);
    console.log(`${this.age}歳です`);
  }
  
}

const namihei = new IsonoFamily("茶風林", 54);

// namiheiに対してgreetメソッドを呼びだす
namihei.greet();

// namiheiに対してinfoメソッドを呼びだす
namihei.info();
```

## メソッド内でメソッドを使う
メソッド内で他のメソッドを呼び出すこともできます。
メソッド内で`this.メソッド名()`と記述することで同じクラスの他のメソッドを使うことができます。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  greet() {
    console.log('ばかもん！');
  }
  
  info() {
    // greetメソッドを呼びだす
    this.greet();
    
    console.log(`声優は${this.voiceActor}です`);
    console.log(`${this.age}歳です`);
  }
  
}

const namihei = new IsonoFamily("茶風林", 54);

namihei.info();
```

## 継承
**継承**とはすでにあるクラスをもとに新しくクラスを作成する方法です。
例えば**IsonoFamilyクラス**から**FugutaFamilyクラス**を継承するとIsonoFamilyクラスの全ての機能を引き継いでクラスを生成します。

継承は`class FugutaFamily extends IsonoFamily`と記述します。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  greet() {
    console.log('ばかもん！');
  }
  
  info() {
    // greetメソッドを呼びだす
    this.greet();
    
    console.log(`声優は${this.voiceActor}です`);
    console.log(`${this.age}歳です`);
  }
  
  // IsonoFamilyクラスを継承してFugutaFamilyクラスを定義
  class FugutaFamily extends IsonoFamily {
  
  }
  
}

const namihei = new IsonoFamily("茶風林", 54);

namihei.info();
```

## 継承したクラスの特性
FugutaFamilyクラスはIsonoFamilyクラスの全ての機能を引き継いでいます。
なのでFugutaFamilyクラスの中身は空でもIsonoFamilyクラスで定義されているメソッドなどはそのまま使うことができます。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  greet() {
    console.log('ばかもん！');
  }
  
  info() {
    // greetメソッドを呼びだす
    this.greet();
    
    console.log(`声優は${this.voiceActor}です`);
    console.log(`${this.age}歳です`);
  }
  
  // IsonoFamilyクラスを継承してFugutaFamilyクラスを定義
  class FugutaFamily extends IsonoFamily {
  
  }
  
}

// 定数taraにFugutaFamilyクラスのインスタンスを代入
const tara = new FugutaFamily("さすがたかこ", 3);

tara.info();
```

## 子クラスにプロパティを追加したい
親クラスで設定したプロパティの他に、子クラスに個別にプロパティを付与させることができます。
その場合は子クラスのコンストラクタの中に`super()`と記述し、その中に親クラスが受け取る引数を記述します。

```php:
class IsonoFamily {
  constructor(voiceActor, age) {
    this.voiceActor = voiceActor;
    this.age = age;
  }
  
  greet() {
    console.log("ばかもん!");
  }
  
  info() {
    this.greet();
    console.log(`声優は${this.voiceActor}です`);
    console.log(`${this.age}歳です`);
  }
}

class FugutaFamily extends IsonoFamily {
  // constructorを追加
  constructor(voiceActor, age, hairStyle) {
    super(voiceActor, age); {
      this.hairStyle = hairStyle;
    }
  }
  
  info() {
    this.greet();
    console.log(`声優は${this.voiceActor}です`);
    // 「髪型は〇〇です」と出力
    console.log(`髪型は${this.breed}です`);
    
    console.log(`${this.age}歳です`);
  }
}

// 3つ目の引数に「"おかっぱ"」を渡す
const tara = new IsonoFamily("さすがたかこ", 3, 'おかっぱ');
tara.info();
```

以上です！