基礎の「き」。Typescriptで学ぶ「クラス」


#はじめに

最近、プログラミング初学者の天敵(？)である「クラス」をようやく退治する気になりました。
という訳で、本記事ではTypescriptにおける「クラス」について基礎から探っていきます。

※執筆者である私は、プログラミング初学者です。そのため扱うレベルが低く、内容に誤りが含まれている可能性もありますが、ご容赦下さい。

##クラスとは？

JavascriptやTypescriptにおいて、オブジェクトを生成する方法は２つあります。
①オブジェクトリテラル
②クラス

①のオブジェクトリテラルは、直接オブジェクトを生成する方法です。

```typescript
//こんな感じ
const point = {
  x: 1,
  y: 2
}
```

一方、クラスとはオブジェクトの設計図のようなものです。
設計図を用意しておき、必要な場面でオブジェクトを生成します。
同じようなオブジェクトが複数必要な場合は、クラスを用いると便利です。

##クラスの定義
一例ですが、クラスは下記のように定義されます。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
```

classというキーワードを使って、クラスが定義されています。
何か色々と見慣れないものが出てきましたが、まずこの部分に着目します。

```typescript
var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
```
定義されたクラスを、上記の記述によって、オブジェクトを作成しています。
**クラスから作成されたオブジェクトは、「インスタンス」と呼ばれます。**

仮にクラスを使用しないとなると、p1やp2といったオブジェクト毎に記述が必要になってしまいます。
クラスの便利さが何となく分かってきました。

console.log()でオブジェクトを確認しておきます。

```typescript
console.log(p1)
console.log(p2)
console.log(Point)

//結果　下記のようなオブジェクトが出来上がる。
{
  "x": 0,
  "y": 10
} 
{
  "x": 10,
  "y": 20
} 

```

##コンストラクタ
次は、下記部分を理解していきます。

```typescript
constructor(x: number, y: number) {
   this.x = x;
   this.y = y;
}
```

これは「コンストラクタ」と呼ばれるメソッドです。コンストラクタは、newでインスタンス(オブジェクト)を作成する際に、最初に呼び出される特別なメソッドです。
**コンストラクタは、オブジェクトの作成に必要な「引数」を渡す役割を担います。**
クラスは設計図です。そこに、データ(引数)を渡すことで、個別のインスタンス(オブジェクト)が作成されます。
データ(引数)は、コンストラクタというメソッドを用いて、クラスに渡されます。

```typescript
var p1 = new Point(0, 10); //0,10 がコンストラクタメソッドの引数に渡される。
var p2 = new Point(10, 20); //10,20がコンストラクタメソッドの引数に渡される。
```

###コンストラクトを用いないと？
コンストラクタを用いない場合、どういう記述になるか気になりました。
こんな感じでしょうか？

```typescript
class Point {
    x: number;
    y: number;
}

var p1 = new Point();
p1.x = 0;
p1.Y = 10
var p2 = new Point();
p2.x = 10;
p2.Y = 20;
```

コンストラクタを用いたほうが、簡単にインスタンスが作成できそうです。

## クラスにメソッドを追加する
クラスにはメソッドを追加することが出来ます。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    //functionは省略して記述される。
    showNumber() {
        console.log(`x:${this.x}, y:${this.y}`);
    }
    //function showNumber() {
        // return new Point(this.x + point.x, this.y + point.y);
    // }  
}

var p1 = new Point(0, 10);
//メソッドを呼び出す。
p1.showNumber()
```

クラス内の値を参照する時は、thisを用います。thisの挙動については、非常に面白いので後述します。


###クラス内のメソッドの活用
クラス内のメソッドは、例えば下記のように活用することができます。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    add(point: Point) {
        return new Point(this.x + point.x, this.y + point.y);
    }
}

var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
var p3 = p1.add(p2); // {x:10,y:30}
```

難しいですね。
ちなみにこれらのコードは、公式ドキュメントから拝借しています。

流れとしては、こんな感じでしょうか。
①p1,p2オブジェクトの作成
②p1オブジェクトの、addメソッドを呼び出す。その際、引数としてp2オブジェクトが渡される。(```p1.add(p2)```の部分)
③addメソッドは、新しいインスタンス(オブジェクト)を作成する(返す)。

そして、p3をconsole.log()で確認すると、```{x:10,y:30}```が出力される。
p1```{x:0,y:10}```にp2```{x:10,y:20}```が加算され、```{x:10,y:30}```になっています。

###クラスを定義すると、自動で型が生成される。
この部分に着目してみて下さい。

```typescript
//引数に型Pointが指定されている。
add(point: Point) {
      //省略
 }
```

引数にPoint型が指定されています。
Point型がいつ定義されているかというと、クラスを定義したタイミングです。
クラスを定義したタイミングで、そのクラスの型が自動で作成されます。
この場合Point型は、x,yプロパティ、addメソッドを持つオブジェクトです。

```add(point: Point)```において、addメソッドの引数には、Point型しか渡せないという指定になっています。

##thisの挙動
ここからは少々マニアック？な話になります。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    showNumber() {
        console.log(`x:${this.x}, y:${this.y}`);
    }
}

var p1 = new Point(0, 10);
p1.showNumber()

//結果
"x:0, y:10"
```
上記のようなコードを、以下のように書き換えてみます。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    showNumber() {
        console.log(`x:${this.x}, y:${this.y}`);
    }
}

var p1 = new Point(0, 10);
const anotherP1 = {
  anotherShowNumber: p1.showNumber
}
anotherP1.anotherShowNumber()

//結果
 "x:undefined, y:undefined" 
```

undefinedになってしまいますね。
オブジェクトの中でthisを使用した場合、thisはそのオブジェクトを示します。
つまりこの場合のthisは、anotherP1を参照しているのです。

例えばこのように記述してみます。

```typescript
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    showNumber() {
        console.log(`x:${this.x}, y:${this.y}`);
    }
}

var p1 = new Point(0, 10);
const anotherP1 = {
  x: 100, //追加
  y: 200, //追加
  anotherShowNumber: p1.showNumber
}
anotherP1.anotherShowNumber()

//結果
 "x:100, y:200" 
```
thisの使用についてはまだまだ追求できそうですが、とりあえずここまでにしておきます。
興味のある方は、class内のメソッドをアロー関数で記述したらどうなるか、試してみると面白いです。

#最後に
長くなったので、続きは別の記事にしておきます。
次回以降は、クラスの継承やメソッドのオーバーライド、アクセス修飾詞、setterとgetterについて触れていきます。

###参考
[TypeScript Deep Drive 日本語版](https://typescript-jp.gitbook.io/deep-dive/future-javascript/classes)
[超TypeScript入門　完全パック(2021)](https://www.udemy.com/course/typescript-complete/)
[TypeScriptハンズオン](https://www.amazon.co.jp/TypeScript%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798065331/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=Typescript&qid=1634033234&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEzSEFSRU1DTjBNSDRMJmVuY3J5cHRlZElkPUEwMzIxMTAzMTAzWUJCUUhJMUdQVCZlbmNyeXB0ZWRBZElkPUFIM0wxVjJRNkJNUjUmd2lkZ2V0TmFtZT1zcF9hdGYmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl)