0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

この記事を書いた理由

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

前提

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

classとは

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

classと関数の違いについて

class は状態を持つのが前提、関数は状態を外に出せる

前置き

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

サンプルコード

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などと記載してデフォルト引数を用意することも可能。

呼び出し時にメソッドに対してのみ、引数を渡すことも可能。

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() メソッドをオーバーライドし、「管理者の〇〇です。〇〇歳です。」と表示されるようにする。

※ まずは動作確認込み、問題なく動いてくれた自分で書いたコードです

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}際です`;
  }
}

実行結果(動作上は問題なし)

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レビューを受けて修正した内容

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の構造が理解できただけでも今後に行きそうな感じはかなりした。

最後に

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

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?