【JavaScript】class構文によるクラス表現【自己学習】

##class構文でできること
JavaScriptには他の言語と異なり、**クラスという概念が存在しない**。

そのため、クラスの概念をfunctionを使って表現していたが、ES6から導入された**class構文**を使うことで、他の言語に慣れた人にもよりわかりやすくクラスの概念を表現できるようになった。

**※ES6からクラスの概念が導入されたわけではない点に注意**

##class構文の書き方

####クラスの定義
クラスを定義する方法は**クラス宣言**と**クラス式**の2種類がある。

#####1. クラス宣言

```class-1.js
class User {  
  //コンストラクタの定義
  //メソッドの定義
}
```

`class クラス名｛｝`と記述することでクラス本体を定義する方法。クラス名は必須で頭文字は大文字にする。 

#####2. クラス式

```class-2.js
let U = class User {  
  //コンストラクタの定義
  //メソッドの定義
};
```

`変数or定数 = class クラス名｛｝`と記述することでクラス本体を定義する方法。クラス名は省略が可能。

変数を使って定義した場合は、クラスを再定義することもできる。


####コンストラクタの定義
**コンストラクタ**とは、生成されるインスタンスのプロパティを設定する特別なメソッド(後述)を指す。

`constructor（引数） {}`と定義することで、インスタンスの生成時に指定した値を引数として受け取ることができる。

引数をインスタンスのプロパティの値とする場合には、{}内に`this.プロパティ名 = 引数`とする。

```constructor-1.js
class User {  
  constructor(name, address) {  //thisにはインスタンス名が入り、プロパティと値を定義している
    this.name = name;        //user1.name = "山田太郎"
    this.address = address;  //user1.address = "東京都"
  }
}

const user1 = new User("山田太郎","東京都");  //インスタンスを生成
console.log(user1);  // > User {name: 山田太郎, address: 東京都}
console.log(user1.address);  // > 東京都
```
インスタンス生成時に指定した「山田太郎」と「東京都」の２つの値をconstructorの引数として受け取り、constructor内でnameとaddressの２つのプロパティの値としてそれぞれ定義されている。

また、インスタンスはオブジェクトなので、生成時に定義したインスタンス名user1を参照すると、「User(クラス名) {name: 山田太郎, address: 東京都}」と出力される。

また、オブジェクトと同じ要領でuser1.addressと参照すると、プロパティの値である「東京都」が出力される。


####メソッドの定義
メソッドとはオブジェクトのプロパティである関数を指す。class内に`メソッド名 {}`で定義する。

メソッドには、コンストラクタで定義したインスタンスのプロパティや同じクラス内の他のメソッドを呼び出して、具体的な処理を記述することができる。

```method-1.js
class User {  
  constructor(name, address) {  
    this.name = name;        
    this.address = address;  
  }

  greet() {
    console.log(`こんにちは！${this.name}です`); //インスタンスのプロパティの値を利用
  }

  introduce() {
    this.greet();  //同じクラスの他のメソッドを利用
    console.log(`${this.address}に住んでいます`);
  }
}

const user1 = new User("山田太郎","東京都"); 
user1.greet(); // > こんにちは！山田太郎です

user1.introduce();  // > こんにちは！山田太郎です
                    // > 東京都に住んでいます
```

メソッド内でインスタンスのプロパティの値を呼び出すためには、`this.プロパティ名`と記述する。また、`インスタンス名.メソッド名()`でメソッドを呼び出すことができる。

また、メソッド内に同じクラス内の別のメソッドを呼び出す場合は、`this.メソッド名`と記述する。

##クラスの継承
####extendsを使ったサブクラスの定義

`extends`で既存のクラスの機能を引き継いだ**サブクラス**を定義することができる。

```extends-1.js
//method-1.jsで定義したUserクラスを継承したBusinessUserクラスを定義
class BusinessUser extends User { 
  
}  

const businessuser1 = new BusinessUser("田中一郎", "大阪府"); //サブクラスのインスタンスを生成
businessuser1.introduce();  // > こんにちは！田中一郎です
                            // > 大阪府に住んでいます
```
上記のコードでは、既存のUserクラスを継承したBusinessUserクラスを定義している。BusinessクラスではUserクラスで定義したコンストラクタやメソッドを呼び出すことができる。

####コンストラクタ、メソッドのオーバーライド
継承元のクラス内のメソッドと同名のメソッドをサブクラス内で定義した場合、そのメソッドは**オーバーライド**（上書き）される。

```override-1.js
class BusinessUser extends User { 
  
  job() {
    console.log("職業は作家です");  //サブクラスで独自に定義したメソッド
  }

  //Userクラスで定義したintroduceメソッドを再定義
  introduce() {
    this.greet();  
    console.log(`${this.address}に住んでいます`);
    this.job();  //メソッドを追加
  }
}  

const businessuser1 = new BusinessUser("田中一郎", "大阪府"); 
businessuser1.introduce();  // > こんにちは！田中一郎です
                            // > 大阪府に住んでいます
                            // > 職業は作家です
```

上記のコードでは、Userクラスで定義したintroduceメソッドに、jobメソッドを追加した上でオーバーライドさせている。結果、introduceメソッドを呼び出すと、「職業は作家です」が追加されて出力される。

コンストラクタもオーバーライドすることができるが、その場合はコンストラクタ内に`super(引数)`と記述することで継承元のプロパティを参照することもできる。

```override-2.js
class BusinessUser extends User { 
  constructor(name, address, job) {
    super(name, address); 
  //super(name, address)でUserクラスの下記のプロパティを参照している
  //this.name = name;
  //this.address = address;
  
    this.job = job; //新しいプロパティを追加
  }
  usersJob() {
    console.log(`職業は${this.job}です`);
  }
  
  introduce() {
    this.greet();  
    console.log(`${this.address}に住んでいます`);
    this.usersJob();  
  }
}  

const businessuser1 = new BusinessUser("田中一郎", "大阪府", "銀行員"); 
businessuser1.introduce();  // > こんにちは！田中一郎です
                            // > 大阪府に住んでいます
                            // > 職業は銀行員です
```
businessuserクラスでは、継承元のプロパティ「name」と「address」をsuperで参照したうえで、独自のプロパティである「job」を追加しオーバーライドしている。