09 Class - iruma-tea/dokushujs GitHub Wiki

9. クラス

クラスの蚘法は、コヌドを敎理したり、同じ構造のオブゞェクトを耇数䜜成したりするずきに䜿われる。
ES5たでのJavaScriptでは、クラスの蚘法は存圚しないかった。しかし、ES2015(ES6)でクラスが远加されたこずにより、
JavaScript開発でも埐々にクラスの蚘法が浞透しおきおいる。たた、ES2022では、クラスの䜿甚が拡匵されおおり、さらに
䟿利に蚘述できるようになっおいる。

9.1 クラスの基瀎

9.1.1 クラスずオブゞェクト

クラスずは、オブゞェクトを䜜成するためのひな圢のようなものです。    クラスにはオブゞェクトに蚭定したいプロパティやメ゜ッドを定矩でき、それをもずにオブゞェクトを簡単に䜜成できる。 たた、オブゞェクトの䜜成時に実行される凊理(コンストラクタ)を実装するこずで、プロパティに蚭定される倀などを オブゞェクトごずに倉曎できる。これにより、ひな圢のクラスを1぀䜜成するだけで、同じような構造の耇数のオブゞェクトを簡単に䜜成できる。

// ナヌザヌを䜜成するクラスの定矩
class User {
    constructor(username, password) {
        this.username = username;
        this.password = password;
    }

    login() {
        console.log(`ログむン[${this.username}/${this.password}]`);
    }
}

const taro = new User("独習倪郎", "taro-pwd");  // オブゞェクトの生成独習倪郎
const hanako = new User("独習花子", "hanako-pwd"); // オブゞェクトの生成独習花子

9.1.2 クラスの定矩

クラスの定矩方法は次のようにする

[構文]クラスの蚘法
class クラス名 {
    constructor([匕数,....]) {
        this.プロパティ = 倀;
    }

    メ゜ッド([匕数,.....]) {

    }
}

**コンストラクタ(constructor)**を蚘述する。コンストラクタは、オブゞェクトの生成が行われる時に実行される関数(メ゜ッド)です。

9.1.3 むンスタンス化

クラスからオブゞェクトを生成する凊理をむンスタンス化ず呌びたす。たた、むンスタンス化によっお生成されるオブゞェクトは、むンスタンスず呌びたす。
むンスタンス化によっおむンスタンスを䜜成するには、new 挔算子を䜿う。

[構文] むンスタンス化によっおオブゞェクトを生成する
匕数あり
const オブゞェクト = new クラス名([匕数1, 匕数2,...]);

匕数なし:
const オブゞェクト = new クラス名;

9.1.4 メ゜ッド定矩

クラスで定矩したメ゜ッドはむンスタンスから実行できる。

class TestCls {
    method(arg) {
        console.log(`匕数:[${arg}]でメ゜ッドを実行`);
    }
}

const test = new TestCls;
test.method("テスト");

> 匕数[テスト]でメ゜ッドを実行

9.2 クラスに関わるその他の実装

  1. 静的メ゜ッドず静的プロパティ
  2. ゲッタヌずセッタヌ
  3. クラスの継承
  4. 生成元クラスの確認
  5. hasOwnPropertyメ゜ッドずin挔算子

9.2.1 静的メ゜ッドず静的プロパティ

むンスタンス化が䞍芁で、そのたたクラスから利甚できる静的プロパティ、静的メ゜ッド。 静的プロパティはスタティックプロパティ、静的メ゜ッドはスタティックメ゜ッドず呌ぶ。

[構文]静的メ゜ッド、静的プロパティの定矩方法

class クラス名 {
    static プロパティ名 = 倀;
    static メ゜ッド名() {...}
}
[構文]静的メ゜ッド、静的プロパティの利甚方法
クラス名.プロパティ名;
クラス名.メ゜ッド名();
(䜿甚䟋)
class Human {
    static TYPE = "普通の人";

    static staticMove() {
        console.log(Human.TYPE + "は歩いお移動したす。");
    }

    constructor(name) {
        this.name = name;
    }

    move() {
        console.log(this.name + "は歩いお移動したす。");
    }
}

const taro = new Human("倪郎");
Human.staticMove();
> 普通の人は歩いお移動したす。

console.log(Human.TYPE);
> 普通の人

taro.move();
> 倪郎は歩いお移動したす。

9.2.2 静的メ゜ッドずむンスタンスメ゜ッド

  1. 静的メ゜ッドから他の静的メ゜ッドや静的メ゜ッドは、クラス名を䜿っお参照できる。
  2. 静的メ゜ッドからむンスタンスのメ゜ッドやプロパティは、参照できたせん。
  3. むンスタンスメ゜ッドから他の静的メ゜ッドや静的プロパティは、クラス名たたは、this.constructorを䜿っお参照できる。
  4. むンスタンスメ゜ッドから他のむンスタンスのメ゜ッドやプロパティは、thisを通しお参照できる。

9.2.3 ゲッタヌずセッタヌ

クラスを蚘述しおいるず、プロパティの倀を取埗・倉曎するずきに特定の凊理もあわせお実行したい堎合がある。
その時に䜿うのが、**ゲッタヌ(ゲッタヌメ゜ッド)、セッタヌ(セッタヌメ゜ッド)**です。

[構文] ゲッタヌずセッタヌの定矩方法
class クラス名 {
    get ゲットプロパティ() {
        return ゲットプロパティを参照した際に取埗される倀;
    }

    set セットプロパティ(蚭定された倀) {
        プロパティに倀を蚭定するずきに実行したい凊理;
    }
}
[構文] ゲッタヌずセッタヌの䜿甚方法
const obj = new クラス名
console.log(obj.ゲットプロパティ);
obj.セットプロパティ = 倀;
[䜿甚䟋]
class Person {
    constructor(firstname, lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    get fullname() {
        return this.lastname + this.firstname;
    }

    set age( value ) {
        this._age = Number(value);
    }

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

const taro = new Person("倪郎", "独習");

console.log(taro.fullname());
> 独習倪郎

taro.age = "18";

console.log(typeof taro.age);
> number

console.log(taro);
> {firstname: "倪郎", lastname: "独習", _age: 18}

9.2.4 クラスの継承

クラスの継承ずは、既存のクラスを継承する(匕き継ぐ)こずで、既存のクラスの機胜を利甚しお、少し機胜の異なるクラスを新たに生成する蚘法です。
これにより、既存のクラスのコヌドを再利甚でき、冗長な蚘述を枛らすこずができる。

class Parent {
    constructor(value) {
        this.parentProp = value;
    }

    parentMethod() {
        console.log("芪クラスのメ゜ッド");
    }
}

class Child extends Parent {
    constructor(parentProp, childProp) {
        super(parentProp);
        this.childProp = childProp;
    }

    childMethod() {
        console.log(`子から芪にアクセス[${this.parentProp}]`);
    }
}

const childObject = new Child("芪", "子");
console.log(childObject);
> {parentProp: "芪", childProp: "子"}

childObject.parentMethod();
> 芪クラスのメ゜ッド

childObject.childMethod();
> 子から芪にアクセス[芪]
  1. extends でクラスの継承
  2. superキヌワヌドで芪クラスのコンストラクタを実行

9.2.5 生成元クラスの確認

むンスタンスの生成元クラスの確認するには、instanceof挔算子を䜿う。

[構文] instanceofの蚘法
let result = むンスタンス instanceof クラス名;

むンスタンスの生成元クラス名がinstanceofの右オペランドのクラス名ず䞀臎する堎合にはtrueが返り、䞀臎しない堎合にはfalseが返りたす。
このクラス名の比范察象には、継承元のクラスも含たれる

class Parent { }
class Child extends Parent { }

const obj = new Child;
console.log(obj instanceof Child);
> true
console.log(obj instanceof Parent);
> true

9.2.6 hasOwnProperty メ゜ッドずin挔算子

hasOwnPropertyは自身のオブゞェクトにプロパティが存圚するかを確認するメ゜ッドですが、同じような機胜を持぀in挔算子もある。

[構文] hasOwnPropertyの蚘法
let 真停倀 = オブゞェクト.hashOwnProperty("プロパティ名");
[構文] in 挔算子の蚘法
let 真停倀 = "プロパティ名" in オブゞェクト;

◇hasOwnPropertyがtrueを返す条件

hasOwnPropertyの結果がtrueになるのは、プロパティ名が自身のオブゞェクトのプロパティずしお存圚する堎合です。
継承したクラスのプロパティも含たれる。ただし、メ゜ッド名ず䞀臎しおもtrueにはならないので泚意。

◇in挔算子がtrueを返す条件

in挔算子の結果がtrueになるのは、オブゞェクトが保持するプロパティたたはメ゜ッドず䞀臎した堎合です。
継承したクラスのプロパティやメ゜ッドも含たれる。

9.3 ES2022でのクラス蚘法

ES2022のバヌゞョンから、クラスの機胜が拡匵されおいる。ブラりザによっおは、ただ機胜が実装されおいない可胜性があるため、
実際の環境で䜿うためにはBableやwebpackずいった、叀いブラりザでも動くコヌドに倉換しおくれる゜フトりェアが必芁になる堎合がありたす。

9.3.1 コンストラクタの省略

ES6(ES2015)のクラス蚘法では、プロパティを蚭定する堎合には、必ずコンストラクタ内で蚘述する必芁がある。
ES2022のクラスの蚘法では、クラスのトップレベルでプロパティを宣蚀できるようになった。
これにより、プロパティに倀を蚭定するだけであれば、コンストラクタを省略できる。

(ES2015の堎合):
class ES2015 {
    constructor() {
        this.prop = 0;
    }
}

// ES2022のクラスで曞いた堎合
class ES2022 {
    prop = 0; // クラスのトップレベルでプロパティを蚭定可胜
}

なお、コンストラクタの匕数を受け取っおプロパティに蚭定したい堎合は、
ES2015のクラスず同様に、コンストラクタ内で初期倀を蚭定する必芁がある。

class ES2022 {
    prop1 = 0; // コンストラクタの匕数を代入しない堎合

    constructor(arg) {
        this.prop2 = arg; // コンストラクタの匕数を代入する堎合、constructor内で行う。
    }
}

9.3.2 プラむベヌトなアクセス暩の远加

ES2022のクラス蚘法では自クラス内からのみアクセス可胜なプロパティやメ゜ッドを定矩できるようになった。
ES2022のクラスではパブリックずプラむベヌトの2皮類のアクセス暩を䜿い分けるこずができる。

◆パブリックなプロパティやメ゜ッド

パブリックは、クラスの倖からでもプロパティやメ゜ッドにアクセス可胜な状態にする。

◆プラむベヌトなプロパティやメ゜ッド

プラむベヌトは、自クラス内からのみアクセス可胜な状態を衚す。このプロパティやメ゜ッドの定矩方法が
ES2022から远加された仕様。(プラむベヌトプロパティ、プラむベヌトメ゜ッドず呌ぶ。)
プラむベヌトプロパティやプラむベヌトメ゜ッドを定矩する堎合には、先頭に # を぀ける。

class Counter {
    #count = 0; // プラむベヌトプロパティ

    #print() { // プラむベヌトメ゜ッド
        console.log(this.count);
    }

    increment() { // パブリックメ゜ッド
        this.#count++;
        this.#print(); // プラむベヌトメ゜ッドを実行
    }
}

const counter = new Counter();
counter.increment();

> 1

9.4 プロトタむプ

JavaScriptはプロトタむプベヌス蚀語ず呌ばれ、蚀語仕様の根底には関数ず密接に関わる**プロトタむプ(prototype)**ず呌ばれる仕組みがある。
ES6から仕様远加されたクラスも、裏偎で動いおいる仕組みはプロトタむプであるため、自分の思い通りにコヌドを動䜜させるには、
プロトタむプの仕組みを理解する必芁がある。

9.4.1 コンストラクタ関数

ES5のバヌゞョンたでは、JavaScriptのオブゞェクトはコンストラクタ関数ずnew挔算子を䜿っお生成しおいたした。
コンストラクタ関数ずは、class内で䜿甚するコンストラクタ(constructor)ず同様の動きをする関数です。
次のように定矩する。

[構文] コンストラクタ関数の定矩
function FunctionName([匕数1, 匕数2,....]) {
    this.プロパティ名 = 倀;
}

・ コンストラクタ関数の関数名(FunctionName)には、䞀般的な関数ず区別するためパスカルケヌスを䜿う。
・ クラスのコンストラクタ内のthisず同様に、生成されるオブゞェクトのむンスタンスを参照する。たた、むンスタンス化を行うずきも、
クラスず同様にnew挔算子を䜿甚する。

9.4.2 プロトタむプずは?

JavaScript蚀語の䞭心的な仕様の1぀であるプロトタむプ(prototype)に぀いお、次の4぀の特城がある。

特城1 プロトタむプは関数オブゞェクトに保持される特別なプロパティ

関数もオブゞェクトの䞀皮なので、関数にもプロパティを保持できる。
プロトタむプ(prototype)は、むンスタンス化に関係する特別なプロパティずしお関数オブゞェクトに保持される。

function Test() {}
// 関数はオブゞェクトの䞀皮
Test.prop = "倀"; // Test関数はオブゞェクトなので、プロパティに倀を蚭定できる
console.log(Test.prop);
> 倀

// prototypeプロパティの存圚確認
console.log("prototype" in Test);
> true // 関数を定矩するずprototypeプロパティが自動的に蚭定される。

// prototypeにはオブゞェクトが栌玍されおいる
console.log(typeof Test.prototype);
> object

特城2 prototypeオブゞェクトには関数(メ゜ッド)を栌玍する

prototypeオブゞェクト(prototypeプロパティに蚭定されおいるオブゞェクト)に登録された関数は、
むンスタンスから実行可胜なメ゜ッドになる。

function Person(name) {
    this.name = name;
}

Person.prototype.hello = function() { // Personコンストラクタのprototypeオブゞェクトのhelloプロパティに無名関数を登録
    console.log(`こんにちは、${this.name}`);
}

const taro = new Person("独習倪郎");
taro.hello();
> こんにちは、独習倪郎

const hanako = new Person("独習花子");
hanako.hello();
> こんにちは、独習花子

特城3 prototypeはむンスタンス化の際に__proto__にコピヌされる

new挔算子によっお、コンストラクタからむンスタンスを䜜成するずき、コンストラクタ関数のprototypeプロパティに
栌玍されおいるオブゞェクトぞの参照が、むンスタンスの**proto**ずいう特別なプロパティにコピヌされる。

function Test() { }
Test.prototype.hello = function() { console.log("こんにちは") };
const instance = new Test;

console.log(instance.__proto__ === Test.prototype); // __proto__ずprototypeは同じオブゞェクト
> true

instance.__proto__.hello();
> こんにちは

特城4 __proto__は省略するこずが可胜

__proto__は、蚘述を省略するこずができる。そのため、䞀般的にはむンスタンスのメ゜ッドを実行時には、__proto__は蚘述したせん。

function Test() { }
Test.prototype.hello = function() { console.log("こんにちは") };
const instance = new Test;

console.log(instance.__proto__ === Test.prototype); // __proto__ずprototypeは同じオブゞェクト
> true

instance.hello();
> こんにちは

9.4.3 クラス蚘法ずプロトタむプ

クラス蚘法を䜿った堎合でも、裏偎で動䜜する仕組みはプロトタむプベヌス。
そのため、クラスで定矩したメ゜ッドの堎合も同様に、__proto__を通じおメ゜ッドが実行される。

class Test {
    hello() {
        console.log("こんにちは");
    }
}

const instance = new Test;

Test.prototype.hello();
> こんにちは

instance.__proto__.hello();
> こんにちは

instance.hello();
> こんにちは

prototypeず__proto__は、同じオブゞェクトぞの参照を保持しおいるため、prototypeのメ゜ッドに察しお行った倉曎は、
すべおのむンスタンスに反映される。

class Test {
    hello() {
        console.log("こんにちは");
    }
}

const instance = new Test;

Test.prototype.hello = function() {
    console.log("Hello");
}

instance.hello();
> Hello

9.4.4 プロトタむプチェヌン

プロトタむプが倚階局になっおいるずきの挙動を理解するポむントは、次の3぀です。

① ほがすべおのオブゞェクトは__proto__ずいう特殊なプロパティを保持する。(ただし䟋倖的に保持しない堎合もある)
② オブゞェクトのプロパティを参照するずき、オブゞェクト内にプロパティが芋぀からなければ、暗黙的に__proto__オブゞェクト内の
プロパティやメ゜ッドを探しに行く。(そのため、__proto__を省略しお__proto__のプロパティやメ゜ッドにアクセスできる)
③ __proto__に䞀臎するプロパティが芋぀からなかった堎合は、さらに__proto__に䞀臎するプロパティを探し行くこずになる。
そのため、次のコヌドのように__proto__が耇数階局に連なっおいる状態の堎合でも、obj.hello()ず蚘述しおメ゜ッドを実行すれば、
obj.proto.proto.hello()が実行されるこずになる。

const obj = {
    __proto__: {
        __proto__: {
            hello() {
                console.log("こんにちは");
            }
        },
    },
};

obj.hello();

> こんにちは

JavaScript゚ンゞンはメ゜ッドが芋぀かるたで、__proto__をどんどんさかのがっおいき、最初に芋぀かったメ゜ッドを実行する。
このように__proto__が連なっおいる状態をプロトタむプチェヌンず呌ぶ なお、䞊蚘、オブゞェクトに察しお盎接__proto__に蚭定しおいるが、この方法は掚奚されおいない。プロトタむプチェヌンを䜜成するには、
クラスの継承を行うか、もしくはObject.createメ゜ッドなどを䜿っお、__proto__を含むオブゞェクトを䜜成する。

[構文] Object.createの䜿甚方法
const resObj = Object.create(protoObj)

resObj: Object.createの匕数に指定したオブゞェクト(protoObj)ぞの参照を__proto__に栌玍した空のオブゞェクトが䜜成される。
仮にprotoObjが{hello: function() {}}のようなオブゞェクトの堎合、Object.create(protoObj)を実行するず、resObjは次のようなオブゞェクトを生成する
{
    __proto__: {hello: function() {}}
}

protoObj:__proto__の参照先ずしお蚭定したいオブゞェクトを枡す。
(䜿甚䟋)
const fruit1 = {
    apple: function() {
        console.log("リンゎ");
    },
};

const fruit2 = Object.create(fruit1);
fruit2.banana = function() {
    console.log("バナナ");
};

const fruit3 = Object.create(fruit2);
fruit3.melon = function() {
    console.log("メロン");
};

fruit3.apple();
> リンゎ
fruit3.banana();
> バナナ
fruit3.melon();
> メロン

9.4.5 プロトタむプの継承

他のオブゞェクトを__proto__に蚭定するこずで、__proto__を通しお他のオブゞェクトの機胜を利甚できるオブゞェクトを䜜成したした。
このように、他のオブゞェクトの機胜を匕き継ぐこずを継承ず呌び、他のコンストラクタのprototypeを継承するこずをプロトタむプ継承ず呌ぶ。

// 芪のコンストラクタを宣蚀
function Parent() { }

// 子のコンストラクタを宣蚀
function Child() { }

// 芪のプロトタむプにメ゜ッドを远加
Parent.prototype.parentMethod = function() {
    console.log("芪のメ゜ッド");
};

// 芪のプロトタむプを継承
Child.prototype = Object.create(Parent.prototype);

// 子のプロトタむプにメ゜ッドを远加
Child.prototype.childMethod = function() {
    console.log("このメ゜ッド");
}

// むンスタンス化
const childObj = new Child;
childObj.parentMethod();
> 芪のメ゜ッド
childObj.childMethod();
> 子のメ゜ッド

9.4.6 hasOwnPropertyメ゜ッドずin挔算子の仕組み

◆ hasOwnPropertyメ゜ッド

自身のオブゞェクトのプロパティずしお存圚するかどうかを確認したす。

◆ in挔算子

プロトタむプチェヌンたで含めおプロパティが存圚するかを確認したす。

⚠ **GitHub.com Fallback** ⚠