🗂

【JavaScrpipt】クラス

に公開2
  • アプリケーションに特化した独自に作成したデータ型をクラスという
    • クラスを定義する際には大文字はじまりが一般的
  • クラスで定義した独自のデータ型の値をインスタンスという
  • 独自に定義した命令をメソッドという
class User { // クラスを定義する書き方
}

const user1 = new User(); // インスタンスを定義して代入する書き方
user1.name = 'Sato'; // プロパティをセットする書き方
user1.age = 16; // プロパティをセットする書き方

const user2 = new User();
user2.name = 'Yoshio';
user2.age = 23;

下記のconstructor関数を使ってシンプルに定義できる

constructor

  • インスタンスから引数を受け取って、実行する関数
  • クラスに独自に定義した命令とも言えるのでメソッドの1つ
class User {
    // constructor(nameFromNew, ageFromNew) {
        // this.name = nameFromNew; // インスタンス毎に違う定数に代入していくことになり、複数のインスタンスでもプロパティとしてつ分かれることが想定されるので、thisで表す
        // this.age = ageFromNew;
    // }
    constructor(name, age) { // クラス内で関数を定義する場合は「function」の記述は不要
        this.name = name; // 左辺:インスタンスのプロパティ、右辺:仮引数で受けっている(引数名は他でも区別できて良いが、同じでも紛らわしいがシンプルで良い。)
        this.age = age;
    }
}

const user1 = new User('Sato', 16); // 直接値を引数として渡す
const user2 = new User('Yoshio', 23);

メソッドの定義

class User {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    getUserString() { // メソッドの定義にはfunctionキーワードは不要
        return `${this.name} ${this.age}`;
    }
}

const user1 = new User('Sato', 16);
console.log(user1.getUserString()); // メソッドもオブジェクト同様、実行できる

別のクラスを作ってクラスに追加

  • クラスごとに実装者が分かれる場合もあるので、クラス内の処理は同じクラスごとで行うように作るのが良さそう
// 追加するクラスを定義
class Score {
    constructor(subject, result) {
        this.subject = subject;
        this.result = result;
    }

    getGrade() {
        return this.result >= 80 ? 'A' : 'B';
    }    

    getScoreString() { // クラス内の処理を返すメソッド
        return `${this.subject}${this.result} ${this.getGrade()}`; // クラス内で定義されたメソッドを使うには、this.を付ける
    }
}

class User {
    constructor(name, score) {
        this.name = name;
        this.score = score;
    }
    
    getUserString() {
        return `${this.name} ${this.score.getScoreString()}`; // this.scoreに対して別のクラスのメソッドを使って情報を取得
    }
}

const user1 = new User('Sato', new Score('科学', 76)); // 引数を含むインスタンスを渡す
console.log(user1.getUserString());

クラスの継承、メソッドのオーバーライド

class Score {
    constructor(subject, result) {
        this.subject = subject;
        this.result = result;
    }

    getGrade() {
        return this.result >= 80 ? 'A' : 'B';
    }

    getScoreString() {
        return `${this.subject} ${this.result} ${this.getGrade()}`;
    }
}

class MathScore extends Score { // extendsを使って、親クラスのインスタンスを作成でき、プロパティ、メソッドを利用できる
    constructor(result) { // 子クラスのconstructorを定義
        super('Math', result); // superを使って、親クラスのコンストラクターを実行でき、引数を渡せる。
    }

    // メソッドのオーバーライド。親クラスと同じ名前のメソッドを定義すると子クラスが優先される。
    getGrade() {
        return this.result >= 70 ? 'A' : 'B';
    }
}

class EnglishScore extends Score {
    constructor(result) {
        super('English', result);
    }
}

class User {
    constructor(name, score) {
        this.name = name;
        this.score = score;
    }

    getUserString() {
        return `${this.name} ${this.score.getScoreString()}`;
    }
}

const user1 = new User('Yoshio', new EnglishScore(49)); // 子クラスを引数として渡す
console.log(user1.getUserString());

クラスのフィールド、静的プロパティ

class User {
    // フィールド = クラスの先頭に設定するプロパティの一覧。
    name;
    age;
    static count = 0; // クラス全体で管理するプロパティにはstaticを付ける。静的プロパティ

    constructor(name, age) {
        this.name = name;
        this.age = age;
        User.count++; // 個々のインスタンスではなく、クラス全体に紐づくstatic付きのプロパティには、クラス名を付ける
    }
    
    getUserString() {
        return `${this.name} ${this.age}`;
    }

    // クラスから直接呼び出せるメソッドはstaticを付ける
    static getUserCountString() { // 静的メソッド
      return `${User.count}個のインスタンスが生成`;
    }
}

const user1 = new User('Sato', 16);
// console.log(User.count); // クラス全体に紐づいた静的プロパティを呼び出している
console.log(User.getUserCountString()); // 静的メソッドを呼び出す場合

console.log(user1.getUserString());

ゲッター、セッター

class User {
    name;
    // _age;
    #age;
    
    constructor(name, age) {
        this.name = name;
        // this._age = age;
        this.#age = age;
    }
    
    get age() { // ゲッター
        // return this._get // 内部的に値を保持するプロパティは別名にする(慣習的に'_')
        return this.#get // パウンド記号だと外部からアクセスでいない(カプセル化)
    }
    
    set age(newValue) { //セッター
        if (newValue < 20) {
            console.log('未成年です。');
            return;
        }
        // this._age = newValue;
        this.#age = newValue;
    }
}

const user = new User('Yoshio', 21);
user.age = 19; // クラスの値をセットするコートを実行する際、セッターに仮引数が渡される
// user._age = 19; // 外部からアクセスできてしまう
user.#age = 19; // カプセル化

console.log(user.age); // クラスの値を取得するコートを実行する際、ゲッターも実行される

メソッドもカプセル化が可能

class User {
    name;
    #age;

constructor(name, age) {
    this.name = name;
    this.#age = age;
}

get age() {
    return this.#age;
}

#isValueValid(newValue) { // パウンド記号でカプセル化
    return newValue < 20 ? false : true;
}

set age(newValue) {
    if (this.#isValueValid(newValue) === false) {
        console.log('未成年です。');
        return;
    }
        this.#age = newValue;
    }
}

const user = new User('Yoshio', 21);
user.age = 19;
console.log(user.age);
console.log(user.#isValueValid(19));

Discussion

junerjuner

独自に定義した命令をメソッドという

そう言ってしまうと toString() が メソッドと言わない様に見えるのでは……?

nishimotonishimoto

ありがとうございます!
まだ、クラスのメソッドとそれ以外のデータ型のもの?の区別が曖昧で理解できておらず、おっしゃっていることを理解できるようになればと思います。