React主体の俺、初めてのclass学習

# この記事を書いた理由
前回の記事の更新で、「オブジェクト指向」とはなんなのか？
ということをメインで記事を書きました。今回はその続きで、「class」とはなんなのか？という点を深掘りしていきたいと思います。
↓前回記事

https://qiita.com/nuhaha_2023/items/36d7268e2f28b78fd373

# 前提
関数型のReactv18 ~ が開発経験の主体のため、classを使用して宣言したことが無く、どういう用途で使われるのかがピンときていません。
たぶん、関数型の考え方に慣れすぎている のが大きいのかなと。
それだけじゃなくて、React が class をあまり推奨していないことも影響している気も....
なのでこの記事を書きながら順次理解していきたいと思います。

# classとは
Classの意味 = 「分類する，整理する，選り分ける」こと。
Class はオブジェクトを「分類（整理）」するための設計図みたいなもの。とのこと。
オブジェクト指向プログラミング（OOP）の概念を実現するための構文。それがclass。

# classと関数の違いについて
class は状態を持つのが前提、関数は状態を外に出せる


# 前置き
コンストラクター (constructor) は new でインスタンスを生成したタイミングで実行される。

# サンプルコード
```javascript
export class Sound {
    constructor(private animal: string, private name: string) {
        console.log(`${name} (${animal}) が鳴くよ`);
    }

    makeSound() {
        if (this.animal === "犬") {
            console.log(`${this.name} は ワンワンワン`);
        } else if (this.animal === "猫") {
            console.log(`${this.name} は にゃーにゃーにゃー`);
        } else {
            console.log(`${this.name} は 知らない音`);
        }
    }
}

// インスタンス生成時に `animal` と `name` を渡す
const myDog = new Sound("犬", "ポチ"); // "ポチ (犬) が鳴くよ"
myDog.makeSound(); // "ポチ は ワンワンワン"

const myCat = new Sound("猫", "ミケ"); // "ミケ (猫) が鳴くよ"
myCat.makeSound(); // "ミケ は にゃーにゃーにゃー"
```

# このコードから読み取る特徴。
- private をつけると クラス内でしか使えない変数 になる（外部からのアクセス不可）
- private をつけると コンストラクタの引数をそのまま this.〇〇 に代入する省略記法 になる
- newでインスタンス化された時に含まれる引数は constructor に渡され、class内でのthis はその constructor の値を参照する
- この記載方法は基本的にnew時の引数が必須となってしまっているが、コンストラクタへの引数のキー名でオプショナル化が可能、。
- private repeat: number = 1などと記載してデフォルト引数を用意することも可能。


# 呼び出し時にメソッドに対してのみ、引数を渡すことも可能。
```javascript
export class Sound {
    constructor(private animal: string) {}

    makeSound(times: number) { // ✅ メソッドに引数 `times` を追加
        let sound = "";
        if (this.animal === "犬") {
            sound = "ワンワンワン";
        } else if (this.animal === "猫") {
            sound = "にゃーにゃーにゃー";
        } else {
            sound = "知らない音";
        }

        for (let i = 0; i < times; i++) { // ✅ メソッドの引数 `times` を使う
            console.log(sound);
        }
    }
}

// インスタンス生成時は `animal` だけ指定
const myDog = new Sound("犬");

// メソッド `makeSound()` に回数を指定
myDog.makeSound(3); 
/*
ワンワンワン
ワンワンワン
ワンワンワン
*/

myDog.makeSound(1);
/*
ワンワンワン
*/
```
# このコードから読み取る特徴。
- constractorに何も処理が含まれない場合であっても、{}自体は省略することができない。(構文エラーになる)
- メソッドに対してのみ、引数を渡すことも可能である。

# chatGPTを使って問題を作成してみた。
出力された問題は以下
 - User クラスを作成し、「名前」「年齢」「メールアドレス」 をプロパティとして持たせること。
クラスのコンストラクタで上記の情報を設定できるようにする。
- introduce() メソッドを作成し、「こんにちは、私は〇〇です。〇〇歳です。」と自己紹介する関数を定義すること。
- isAdult() メソッドを作成し、年齢が 18 歳以上なら true を、それ未満なら false を返すこと。
- AdminUser クラスを作成し、User を継承すること。管理者権限 (isAdmin: boolean) を追加する。
AdminUser では introduce() メソッドをオーバーライドし、「管理者の〇〇です。〇〇歳です。」と表示されるようにする。


※ まずは動作確認込み、問題なく動いてくれた自分で書いたコードです
```javascript
export class User {
  constructor(
    private name: string,
    private age: number,
    private address: string
  ) {
    this.name = name;
    this.age = age;
    this.address = address;
  }

  introduce() {
    return `こんにちは、私は${this.name}です。${this.age}歳です`;
  }
  isAdult() {
    return this.age >= 18;
  }
}

export class AdminUser extends User {
  constructor(
    private name: string,
    private age: number,
    private address: string,
    private isAdmin: boolean
  ) {
    super(name, age, address);
  }
  introduce(): string {
    return `管理者の${this.name}です。${this.age}際です`;
  }
}

```
実行結果(動作上は問題なし)
```javascript
import { AdminUser, User } from "./class";
import "./styles.css";

export default function App() {
  const user = new User("太郎", 40, "tokyo");
  console.log(user.introduce()); //こんにちは、私は太郎です。40歳です
  console.log(user.isAdult()); // true

  const isAdmin = new AdminUser("太郎", 40, "tokyo", true);
  console.log(isAdmin.introduce()); //管理者の太郎です。40際です

  const notAdmin = new AdminUser("太郎", 40, "tokyo", false);
  console.log(notAdmin.introduce()); //管理者ではありません

  return <div className="App"></div>;
}

```

### chatGPTレビューを受けて修正した内容

```javascript
export class User {
  constructor(
    private name: string,
    private age: number,
    private address: string
  ) {}

  // コンストラクターからのnameを直接取得するゲッター関数を用意 
  getName(): string {
    return this.name;
  }
  // コンストラクターからのageを直接取得するゲッター関数を用意 
  getAge(): number {
    return this.age;
  }

  // コンストラクターから直接参照せずにgetter関数を使用したメソッド
  introduce() {
    return `こんにちは、私は${this.getName()}です。${this.getAge()}歳です`;
  }
  isAdult() {
    return this.age >= 18;
  }
}

// 新しくAdminUserクラスを定義して、extendsでUserを継承する。
export class AdminUser extends User {
  constructor(
    name: string,
    age: number,
    address: string,
    private isAdmin: boolean
  ) {
    super(name, age, address);
  }
  getIsAdmin() {
    return this.isAdmin;
  }
  
  introduce(): string {
    if (this.getIsAdmin())
      return `管理者の${this.getName()}です。${this.getAge()}際です`;
    return "管理者ではありません";
  }
}

```

## super()とは
- 親クラスのコンストラクタを(extends先)、「継承したクラスでも」実行するためのもの。
- 親クラス (User) に定義されたプロパティ(コンストラクタの引数として受け取って、this.〇〇に保存されたもの)を子クラス (AdminUser) でも使えるようになる！
- 継承時に、superを書き忘れていると「親クラスのコンストラクタを呼んでない」 というエラーが発生する。
- ゲッター関数を親側で定義していても、superを書き忘れると呼び出し時にundefindになる。

## この記事を書いてわかったこと
- コンストラクター (constructor) は new でインスタンスを生成したタイミングで実行される。
- コンストラクターの引数を直接取得したい場合はgetプロパティ名としてゲッター関数を用意すると良い。
- コンストラクターの引数はprivateとしている場合はthisに含まれるため良い。
- メソッドなので関数と同様にreturnを使用する
- ゲッター関数とはなにかということ
- ただあまりクラスのインスタンス化した時に直接引数を渡すケースよりはメソッドの引数にすることのほうがケースとしては多そう。
- さまざまなオブジェクト指向言語で使われる概念が多いので、classの構造が理解できただけでも今後に行きそうな感じはかなりした。

# 最後に
割と書いてて、今後に生きる学習になったなぁと思います、ありがとうございました。