【JavaScriptの超基本】クラスの定義やメソッド・継承について簡単に解説

#概要

この記事では、JavaScriptのクラスについて、超基本的な知識をメモ的にまとめています。
自分用の備忘録なのであしからず。

今回は、FCバルセロナの選手を紹介するプログラムを、クラスを用いて書いてみます。



#目次

- [クラスとは](#クラスとは)
    - [クラスの定義](#クラスの定義)
    - [インスタンスの生成](#インスタンスの生成)
- [コンストラクタ](#コンストラクタ)
    - [プロパティと値を追加する](#プロパティと値を追加する)
    - [コンストラクタに引数を渡す](#コンストラクタに引数を渡す)
- [メソッド](#メソッド)
    - [メソッド内でメソッドを使う](#メソッド内でメソッドを使う)
- [クラス式](#クラス式)
- [クラスの継承](#クラスの継承)
    - [メソッドの追加](#メソッドの追加)
    - [オーバーライド](#オーバーライド)
        - [メソッドのオーバーライド](#メソッドのオーバーライド)
        - [コンストラクタのオーバーライド](#コンストラクタのオーバーライド)
- [まとめ](#まとめ)




#クラスとは

**クラス**とは特別な関数のことで、**オブジェクトを効率良く作成するための設計図**です。

JavaScriptは、**オブジェクト指向の言語**です。
オブジェクト指向とは、**プログラムを現実のモノのように構成する指向**のことです。

このようにオブジェクトをモノとして考えたとき、そのモノの設計図となるのがクラスです。

Webサービスなどでは、大量のオブジェクトを用いてデータを扱っています。
例えば、ログインが必要なサービスでは、ユーザーに関するデータをオブジェクトとして用いていることが多いです。

このように大量のデータを毎回ゼロから作成するのは大変なので、**クラスを用いることで効率よくオブジェクトを作成**しています。




##クラスの定義

クラスを宣言する構文は以下に示す通りです。

```index.js
class クラス名 {
    //コンストラクタやメソッドなどをここで定義
}
```

`class クラス名`と書いた後、`{ }`の中にコンストラクタやメソッドを定義していきます。
コンストラクタやメソッドについては後ほどまとめていきます。

では、空のクラスを用意してみましょう。

```index.js
class Barcelona {

}
```

これで「Barcelona」クラスが用意できました。




##インスタンスの生成

設計図となるクラスを用意できたら、その設計図からオブジェクトを生成します。

クラスからオブジェクトを生成するには`new クラス名()`とします。

クラスから生成されたオブジェクトのことを「 **インスタンス**」と呼びます。

先ほど用意したBarcelonaクラスからインスタンスを生成してみます。

```index.js
class Barcelona {

}

//インスタンスを生成し、定数に代入
const barca = new Barcelona();
```

Barcelonaクラスから生成されたインスタンスをBarcelonaインスタンスと呼びます。
生成したインスタンスは、定数などに代入して用いられます。

この状態で定数barcaを出力すると**空のオブジェクト**が出力されます。




#コンストラクタ

「**コンストラクタ**」とは、**classによって生成されるオブジェクトの生成や初期化を行う機能**です。
コンストラクタはクラスで1つしか定義できません。

クラスの`{ }`の中に`constructor() { }`と書くことで定義することができます。

```index.js
class Barcelona {
    //コンストラクタの定義
    constructor() {
        処理
    }
}

const barca = new Barcelona();
```

コンストラクタの`{ }`の中には、処理を記述します。
ここに書いた処理は、インスタンスが生成された直後に実行されます。

また、コンストラクタの処理はインスタンスが生成されるたびに毎回実行されます。

コンストラクタに処理を書いてみます。

```index.js
class Barcelona {
    constructor() {
        //コンストラクタの処理
        console.log('Hola!');
    }
}

const barca = new Barcelona();
//Hola!
```

コンストラクタの中に「Hola!」と出力する処理を書いてみました。

インスタンスを生成した後に、ファイルを実行するとコンソールにはコンストラクタの中に書いた処理が実行されました。




##プロパティと値を追加する

クラスの目的はオブジェクトを生成することなので、プロパティと値を追加する方法を学びます。

コンストラクタの中で`this.プロパティ名 = 値`とすることで、生成されたインスタンスにプロパティと値を追加することができます。

また、インスタンスはオブジェクトなので、コンストラクタの中で追加した値は、`インスタンス.プロパティ`とすることでクラスの外で使用することができます。

練習で`name`プロパティに`Messi`と追加してみます。さらにそれを出力します。

```index.js
class Barcelona {
    constructor() {
        this.name = 'Messi';
    }
}

const barca = new Barcelona();

//生成したインスタンスを出力
console.log(barca);
//{name: Messi}

//追加した値の出力
console.log(barca.name);
//Messi
```

コードを見てみていきます。

`constructor`の中で`this.name = 'Messi';`とすることで、`{name: 'Messi'}`というオブジェクトが生成されています。

次に`const barca = new Barcelona();`で`barca`インスタンスを生成しています。`constructor`はインスタンスが生成された時に走るので、この時点で`constructor`の中で定義した`{name: 'Messi'}`が生成されます。

そのため、生成した`barca`インスタンスを出力すると、`{name: 'Messi'}`と表示されます。

また、`barca.name`とすることで、値を取り出すこともできています。





##コンストラクタに引数を渡す

コンストラクタの`()`内では引数を受け取るができます。関数と同じように、`()`の中に引数名を記述することで、その引数をコンストラクタの処理内で使用できます。

引数を渡すには、インスタンスを生成する際のクラス名のあとの`()`に値を追加します。

先ほどの例を引数を使ってアップデートします。

```index.js
class Barcelona {
    //引数を受け取る
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
}

//引数を渡す
const barca = new Barcelona('Messi', 10);

//それぞれの値を取り出す
console.log(barca.name);
//Messi

console.log(barca.uniformNumber);
//10
```

コードを見てみます。

先ほどと変わった部分は、`constructor`の定義部分です。引数に`name`と`uniformNumber`を渡すように書き換えています。

そのため、インスタンスを生成する際には、`const barca = new Barcelona('Messi', 10);`というように2つの引数を渡して生成しなければいけません。

出力は、先ほどの例と同様の結果が得られます。




#メソッド

「**メソッド**」とは、**クラスの動作を定義するもの**になります。

class構文の中では、クラスに対して自由にメソッドを定義できます。このクラスに定義したメソッドは作成したインスタンスが持つ動作となります。

実は`constructor`は、**初期化時に呼ばれる特殊なメソッド**です。`constructor`では、情報としてプロパティを設定しました、メソッドでは動作を設定します。

メソッドはクラスの`{ }`の中で`メソッド名() { }`とすることで定義できます。メソッドは関数のようなもので、`{ }`の中に処理を書きます。
処理の部分では、`return`を用いて値を返すこともできます。
メソッドを呼び出す際は、`インスタンス.メソッド名()`とします。

先ほどの例に、`introduce`メソッドを追加し、呼び出します。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    //メソッドの定義
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

const barca = new Barcelona('Messi', 10);
//メソッドの呼び出し
barca.introduce();
//FC Barcelonaの背番号10はMessiです
```

では、変わった部分をみていきましょう。

`class`内で`introduce`メソッドを定義しています。
処理の中身は、引数で受け取った値が入ったオブジェクトを使って、「FC Barcelonaの背番号〇〇は△△です」とコンソールに表示させる処理を書いています。

このようにメソッド内でインスタンスの値を使うときは、`this.プロパティ名`とすることで定数や変数のように扱うことができます。

インスタンスの生成時には、2つの引数を渡します。

`barca.introduce();`でクラスで定義したメソッドを呼び出しています。メソッドの中で書いた処理が実行され、「FC Barcelonaの背番号10はMessiです」と出力されます。




#メソッド内でメソッドを使う

メソッド内で他のメソッドを呼び出すには`this.メソッド名()`と書きます。

今回は例として、先ほどの例に新しいメソッドを追加して、それを呼び出してみます。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
    //新しいメソッドを定義
    introduceAdd() {
        console.log('FC Barcelonaの選手紹介');
        //メソッド内でメソッドを使う
        this.introduce();
    }
}

const barca = new Barcelona('Messi', 10);
//新しく作成したメソッドを呼び出す
barca.introduceAdd();
// FC Barcelonaの選手紹介
// FC Barcelonaの背番号10はMessiです
```

追加したコードを見ていきます。

`introduceAdd`というメソッドを定義しています。`introduceAdd`内では、`introduce`メソッドを呼び出すために、` this.introduce();`としています。

`introduceAdd`メソッドを呼び出して実行すると、しっかりと処理が実行されました。




#クラス式

「**クラス式**」とは、クラスを定義するもう一つの方法です。

クラス式を用いると、**名前なしでクラスを定義する**ことができます。

書き方は関数式のように、定数や変数にクラスを代入します。

これまでのコードをクラス式で書き換えてみましょう。

```index.js
//クラス式でクラスを定義
let Barcelona = class {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

const barca = new Barcelona('Messi', 10);
console.log(barca.name);
```

コードの変わった部分をみてみます。

これまで`class Barcelona {}`と定義していたところを、`let Barcelona = class { }`と書き換えました。

その他は変更はありません。

コードは問題なく実行されます。




#クラスの継承

「**継承**」とは、**すでにあるクラスをもとに新しくクラスを作成する方法**のことです。
継承元になるクラスを親クラス、新しく作成するクラスを子クラスと呼び、`class 子クラス名 extends 親クラス { }`と書きます。

`Barcelona`クラスを継承して`Forward`クラスを作成してみます。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

//クラスを継承
class Forward extends Barcelona {

}

//インスタンスを生成
const forward = new Forward('Messi', 10);

//継承したメソッドを呼び出す
forward.introduce();
//FC Barcelonaの背番号10はMessiです
```

コードの変更部分を見てみましょう。

`class Forward extends Barcelona { }`でクラスを継承しています。定義したクラスの中には何も処理を書いていません。

その後、`Forward`クラスから`forward`インスタンスを生成しています。この時、親クラスに書かれている`constructor`では引数を2つ受け取るので、インスタンスを生成する際にも2つの引数を渡します。

`forward.introduce();`で継承したメソッドを呼び出しています。`Forward`クラスの中では、何も処理を記述していませんが、`Barcelona`クラスを継承しているので、`introduce`メソッドを使うことができます。

このように、作成した子クラスは、親クラスの全ての機能を引き継いでいるため、親クラスに定義されているメソッドを使用することができます。




##メソッドの追加

継承して作成した子クラスにも、独自のソッドを追加することができます。
子クラスで定義した独自のメソッドは、親クラスから呼び出すことは出来ないので注意しましょう。

先ほどの例で、子クラスにメソッドを追加してみます。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

class Forward extends Barcelona {
    //独自のメソッドを追加
    introducePosition() {
        console.log('ポジションはFWです');
    }
}

//インスタンスを生成しメソッドを呼び出す
const forward = new Forward('Messi', 10);
forward.introducePosition();
//ポジションはFWです
```

コードを見てみましょう。

`Barcelona`クラスを継承した`Forward`クラスの中で`introducePosition() { }`というメソッドを記述しています。このメソッドでは、ポジション紹介をコンソールに出力する処理を書いています。

先ほどの例と同じく`forward`インスタンスを生成します。

最後に、`forward.introducePosition();`でメソッドを呼び出します。すると、メソッドの処理が実行され、コンソールに文章が出力されます。




##オーバーライド

###メソッドのオーバーライド

子クラスにすでに親クラスに存在するメソッドと同じ名前のメソッドを定義すると、子クラスのメソッドが優先して使用されます。
これを「**オーバーライド**」と呼び、子クラスのメソッドが親クラスのメソッドを上書きしています。

先ほどの例で、introduceメソッドをオーバーライドしてみます。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

class Forward extends Barcelona {
    //introduceメソッドをオーバーライド
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
        //introducePositionメソッドを追加
        this.introducePosition();
    }
    introducePosition() {
        console.log('ポジションはFWです');
    }
}

//インスタンスを生成しメソッドを呼び出す
const forward = new Forward('Messi', 10);
forward.introduce();
// FC Barcelonaの背番号10はMessiです
// ポジションはFWです
```

変わった部分を見ると、子クラスである`Forward`クラスの中で、親クラスの中でも定義されている`introduce`メソッドの中身を書き換えています。

このメソッドを`forward.introduce();`として呼び出すと、追加で書いた処理も実行されていることがわかります。

今この状態では、`Barcelona`クラスの中の`introduce`メソッドと`Forward`クラスの中の`introduce`メソッドが存在するわけですが、同じメソッドでも実行される処理は違うという状態になっています。




###コンストラクタのオーバーライド

コンストラクタもオーバーライドすることができます。
オーバーライドすることで、子クラスにプロパティを追加したりすることができます。

子クラスでコンストラクタをオーバーライドする際は、1行目に`super()`と記述します。
この`super()`の部分が親クラスのコンストラクタを呼び出しています。

そのため、親クラスのコンストラクタが引数を受け取る場合は、`super`の後ろの`( )`の中に引数を渡す必要があります。

これまでの例に、`position`プロパティを追加してみます。それに伴い、メソッドの内容も少し書き換えます。

```index.js
class Barcelona {
    constructor(name, uniformNumber) {
        this.name = name;
        this.uniformNumber = uniformNumber;
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
    }
}

class Forward extends Barcelona {
    //コンストラクタをオーバーライド
    constructor(name, uniformNumber) {
        super(name, uniformNumber);
        this.position = 'FW'
    }
    introduce() {
        console.log(`FC Barcelonaの背番号${this.uniformNumber}は${this.name}です`);
        this.introducePosition();
    }

    //positionプロパティを使用
    introducePosition() {
        console.log(`ポジションは${this.position}です`);
    }
}

const forward = new Forward('Messi', 10);

forward.introduce();
// FC Barcelonaの背番号10はMessiです
// ポジションはFWです

//インスタンスを出力
console.log(forward);
//{ name: 'Messi', uniformNumber: 10, position: 'FW' }
```

変更部分をみてみます。

`Forward`クラスにコンストラクタを追加しました。

コンストラクタの中では、まず`super(name, uniformNumber);`と書くことで、親クラスのコンストラクタを呼び出しています。
これに、`this.introducePosition();`と書くことで、プロパティと値を追加しています。

実際に生成した`forward`インスタンスを`console.log(forward);`といて出力すると、子クラスで追加したプロパティと値が追加されたオブジェクトが表示されます。





#まとめ

今回は以上となります。
コードが長くなってくると混乱してくるので、少しずつ理解していきましょう。

最後まで読んでいただきありがとうございました。ではまた。