【JavaScript】クラス・コンストラクタを使いたい！

こんにちは、本日もJavaScriptについて学習したことをメモしていきます。

今回はクラス・コンストラクタの基本的な使い方について記述しました。
クラス・コンストラクタって何？と言った方の参考に少しでもなれたら幸いです。

また至らない点がありましたら、ご指摘いただけると幸いです。


#クラスとは
**オブジェクトを作り出す為の「特別な関数」**になります。
よく使われるイメージとしては、**オブジェクトの「設計図」**です。

オブジェクトは名前（プロパティ）と値をセットにして格納することができますが、**このクラス（設計図）を使えば、共通のプロパティやメソッドを保持したオブジェクトを必要に応じて生成することができます。**

また、クラスからオブジェクト生成することを**「インスタンス化」**といい、生成されたオブジェクトは**「インスタンス」**と言います。


#コンストラクタとは
クラス内で使える、**プロパティの設定・初期化を行うメソッド**です。
作られたインスタンスオブジェクトは、そのままでは空なので、共通するプロパティを設定し、値を代入する役割を担っています。


と、ざっくりとした概要はここまでにして早速コードを見ていきましょう。

#クラスの活用場面

例えば、商品情報を持った複数のオブジェクトを作りたいとします。
クラスを使わずに、オブジェクトを用意するとした場合、以下のようなコードになります。

```js
const product1 = {
    name: '商品A',
    price: 300,

    display(){
        console.log(`商品名:${this.name} 値段:${this.price}円`);
    }
}

const product2 = {
    name: '商品B',
    price: 400,
    
    display(){
        console.log(`商品名:${this.name} 値段:${this.price}円`);
    }
}

const product3 = {
    name: '商品C',
    price: 1000,
    
    display(){
        console.log(`商品名:${this.name} 値段:${this.price}円`);
    }
}
```

このように各オブジェクトには、共通してname、priceプロパティや商品情報を表示するメソッドが定義されていますが、
**以下のような問題があります。**

・同じ記述が多いので、商品が増えれば**コードが長くなる。**
・displayメソッドは同じ処理だが、オブジェクトごとに定義しているので、**無駄なメモリ消費**をしている。
・修正箇所が出た場合、**変更箇所が多くて大変。**

これらの問題点を解決してくれるのが、「クラス」です。
この商品の例をクラスを使って実装すると、以下のようになります。




```js
//class関数を使って、Productコンストラクタを作る
class Product{
    constructor(name,price){
        //プロパティの初期化
        this.name = name;
        this.price = price;
    }

    //各インスタンスから参照されるメソッドを定義
    display(){
        console.log(`商品名:${this.name} 値段:${this.price}円`);
    }
}

//以下、インスタンスオブジェクトの生成
const product1 = new Product("商品A",300);
const product2 = new Product("商品B",400);
const product3 = new Product("商品C",1000);
```
同じ記述が減り、先ほどよりすっきりしましたね。
では、具体的にどのような構造になっているか、上から順番に見ていきます。

#クラスの構造
##① class

まず、「class」についてです。

```js
class Product{...}
```

class（クラス）とは冒頭に述べたように、**インスタンスを作る為の「特別な関数」「設計図」**になります。

なのでコード上のclassは、これから**class関数を使ってインスタンス定義を行いますよ！**と宣言しているわけです。

そして、**クラス名は他の関数と区別できるよう頭文字を大文字**にします。


##② constructorメソッド
冒頭で述べたコンストラクタの説明は、このメソッドのことを指します。
また、**constructorメソッドはクラスが呼び出された際、最初に実行されます。**
具体的に見ていきましょう。

```js
class Product{
    constructor(name,price){
        //thisの設定、プロパティの初期化
        this.name = name;
        this.price = price;
    }
}
```

実行された際の処理は以下になります。

①thisの設定
②プロパティの初期化

まずthisの設定ですが、
**``new Product(引数);（インスタンス化）``をすることで作られる空オブジェクト（インスタンス）をthisに設定します。**
この処理によって、product1のnameプロパティ、product2のnameプロパティ、といった具合に各インスタンスごとのプロパティを準備することができます。

続いて、プロパティの初期化です。
準備した各プロパティに、インスタンス化で受け取った**引数を代入していきます。**


このようにconstructorメソッドは、インスタンス化された際に最初に実行され、**インスタンスのプロパティの設定と初期化**を行います。


##③ メソッド
続いて、メソッドの定義です。
インスタンスには、「dispaly」という商品情報を表示するメソッドを持たせたいので、
**constructorメソッドと同じ階層に、dispalyメソッドを定義してあげます。**

```js
class Product{
    constructor(name,price){...} //省略

    //各インスタンスが参照するメソッドを定義
    display(){
        console.log(`商品名:${this.name} 値段:${this.price}円`);
    }
}
```
このように定義することで、生成されたインスタンスはdisplayメソッドを使えるようになります。

ではここで、**「class内でメソッドを定義する方法」と**、最初の例にあげた**「オブジェクトごとにメソッド定義する方法」の違い**を見ていきましょう。

簡単に言うと、**１つのメソッドを共有して参照するか、各メソッドをそれぞれ参照するかという違い**があります。

コードではイメージしづらいので、下の図を見てみましょう。
![object.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769210/b323e273-9679-b50f-fe4e-b55194ec2513.png)
オブジェクトごとにdisplayを定義した場合、実行時は**それぞのオブジェクト内のdisplayメソッドを参照して実行する**ことになります。

メソッド内の処理は同じですが、メソッドはそれぞれ独立して定義されています。
その為、**オブジェクトが増えればdisplayメソッドも増えていく**ので、
**結果的に、無駄なメモリ消費をしている**ことになります。

対して、class内でメソッドを定義した場合は以下になります。
![class.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769210/82e1a864-f0d0-48aa-3f58-a2b9f9354e2a.png)
このようにclassで定義して実行すると、**全てのインスタンスはclass内で定義したdispalyメソッドを参照します。**

つまり、**メソッドの定義は１つのだけで、インスタンスはそのメソッドへ参照を保持している形になるので、結果としてメモリの節約に繋がります。**


#④ new演算子
クラスは関数ですが、**一般的な関数とは異なり実行する際は、クラス名の前に「new」をつけます。**

```js
new ClassName(arguments);
```
このようにしてあげることで、インスタンス化が行われます。

コード上では見えませんが、インスタンス化の処理の流れを簡単に説明すると、
**dispalyメソッドの参照先を保持した新しい空オブジェクト（インスタンス）が作られ、constructorメソッドでプロパティを初期化し、最終的にそのオブジェクトが返されます。**

```js
const product1 = new Product("商品A",300);
```
そして帰ってきたオブジェクトは、今回の場合product1変数に代入されます。

ということで、**まとめると「new」はインスタンス化に使用するキーワードで、新しいオブジェクトを生成し、クラス内でメソッドの参照やプロパティを設定し、最終的にオブジェクトを返すことができる**、ということですね。


長くなりましたが、クラスの定義から実行までの流れは以上になります。




#classのホイスティングについて
classは関数ですが、一般的な関数とは異なり、ホイスティングは発生しません。
なのでclass宣言よりも前に呼び出しをするとエラーになります。

```js
const product1 = new Prodcut(); // ReferenceError

class Product {}
```

ホイスティングってなんだっけ？という方は、[ホイスティングとは？]("https://qiita.com/TakanoriOkawa/items/ab51d9bf33cbd2f82501")をご覧ください。



#まとめ
ここまでお読みいただきありがとうございます。
今回はクラスの基本的な部分について記述しました。
JavaScriptのクラスは他の言語と比べると癖があるようなので、何度も記述して使いこなせるようになりたいと思います。
また至らない点がありましたら、ご指摘いただけると幸いです。

#参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/constructor
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new
https://qiita.com/daisu_yamazaki/items/109e3044645c4fe2925d
https://qiita.com/takeharu/items/809114f943208aaf55b3
https://qiita.com/JPNYKW/items/248f99c94c00c3d1aa27