クラス

「クラス」と一言にいってもさまざまであるため、ここでは構造動作(メソッド)、状態をもつもののことを示すことにします。 また、この章では概念を示す場合はクラスと呼び、クラスに関する構文(記述するコード)のことをclass構文と呼びます。

クラスとは、動作を持った構造を定義でき、その構造からインスタンスを作成し、そのインスタンスは状態を持てるものです。 とても抽象的なことに見えますが、これは今までオブジェクトや関数を使って表現してきたものにも見えます。 実際にJavaScriptではES2015より前まではclass構文はなく、関数を使いクラスのようなものを表現して扱っていました。

ES2015でclass構文が導入されましたが、このclass構文で定義したクラスは一種の関数オブジェクトです。 class構文ではプロトタイプベースの継承の仕組みの上に関数でクラスを表現しています。 そのため、class構文はクラスを作るための関数定義や継承をパターン化した書き方といえます。糖衣構文

JavaScriptでは関数で学んだことの多くはクラスでもそのまま適応されます。 また、関数の定義方法として関数宣言文と関数式があるように、クラスにもクラス宣言文とクラス式があります。 この章では、class構文でのクラスの定義や継承、クラスの性質について学んでいきます。

クラスの定義

クラスを定義するにはclass構文を使いますが、クラスの定義方法にはクラス宣言文とクラス式があります。

まずは、クラス宣言文によるクラスの定義方法を見ていきます。

クラス宣言文ではclassキーワードを使い、class クラス名{ }のようにクラスを定義できます。 クラスは必ずコンストラクタを持ち、constructorという名前のメソッドとして定義します。 コンストラクタとは、そのクラスからインスタンスを作成する際にインスタンスに関する初期化を行うメソッドです。

class MyClass {
    constructor() {
        // コンストラクタ関数の処理
    }
}

もうひとつの定義方法であるクラス式は、クラスを値として定義する方法です。 クラス式ではクラス名を省略できます。これは関数式における匿名関数と同じです。

const MyClass = class MyClass {
    constructor() {}
};

const AnnonymousClass = class {
    constructor() {}
};

クラスは必ずコンストラクタを持ちますが、コンストラクタ関数内で何も処理がない場合はコンストラクタの記述を省略できます。

class MyClassA {
    constructor() {
        // コンストラクタの処理が必要なら書く
    }
}
// コンストラクタの処理が不要な場合は省略できる
class MyClassB {

}

クラスのインスタンス化

クラスはnew演算子でインスタンスであるオブジェクトを作成できます。 class構文で定義したクラスからインスタンスを作成することをインスタンス化と呼びます。

class MyClass {
}
// `MyClass`をインスタンス化する
const myClass = new MyClass();
// 毎回新しいインスタンス(オブジェクト)を作成する
const myClassAnother = new MyClass();
// それぞれのインスタンスの参照は異なる
console.log(myClass === myClassAnother); // => false

このままでは何もできない空のクラスなので、値を持ったクラスを定義してみましょう。

クラスではインスタンスの初期化処理をコンストラクタ関数で行います。 コンストラクタ関数はnew演算子でインスタンス化されるときに暗黙的によばれ、 コンストラクタのなかでのthisはこれから新しく作るインスタンスオブジェクトとなります。

次のコードではx座標とy座標の値をもつPointというクラスを定義しています。 コンストラクタ関数(constructor)の中でインスタンスオブジェクト(this)のxyプロパティに値を代入して初期化しています。

class Point {
    // コンストラクタ関数の仮引数として`x`と`y`を定義
    constructor(x, y) {
        // コンストラクタ関数における`this`はインスタンスを示すオブジェクト
        // インスタンスの`x`と`y`プロパティにそれぞれ値を設定する
        this.x = x;
        this.y = y;
    }
}

このPointクラスのインスタンスを作成するにはnew演算子を使います。 new演算子には関数と同じように引数を渡すことができます。 new演算子の引数はクラスのconstructorメソッド(コンストラクタ関数)の仮引数に渡されます。 そして、コンストラクタのなかではインスタンスオブジェクト(this)の初期化処理を行います。

class Point {
    // 2. コンストラクタ関数の仮引数として`x`には`3`、`y`には`4`が渡る
    constructor(x, y) {
        // 3. インスタンス(`this`)の`x`と`y`プロパティにそれぞれ値を設定する
        this.x = x;
        this.y = y;
        // 4. return文がなくてもコンストラクタは自動的に`this`を返す
    }
}

// 1. コンストラクタ関数には引数として`3`と`4`渡してインスタンス化
const point = new Point(3, 4);
// 5. `Point`のインスタンスである`point`の`x`と`y`プロパティにはそれぞれ値が入る
console.log(point.x); // => 3
console.log(point.y); // => 4

コンストラクタ(construtorメソッド)の中では明示的にreturn文が出てきていません。 通常の関数とは異なり、コンストラクタ関数は暗黙的にthis(インスタンスオブジェクト)を返します。 明示的にreturn文を書くこともできますが、通常はコンストラクタ関数内でreturn文は書きません。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        // コンストラクタ関数内では暗黙的に`this`が返される
        return this;
    }
}

const point = new Point(3, 4);
console.log(point.x); // => 3
console.log(point.y); // => 4

[Note] クラス名は大文字で始める

JavaScriptでは慣習としてクラス名は大文字で始まる名前を付けます。 これは、変数名にキャメルケースを使う慣習があるのと同じで、名前自体には特別なルールがあるわけではありません。 クラス名を大文字にしておき、そのインスタンスは小文字で開始すれば、名前が被らないため合理的な理由で好まれています。

class Thing {}
const thing = new Thing();

[コラム] class構文と関数でのクラスの違い

ES2015より前はこれらのクラスをclass構文ではなく、関数で表現していました。 その表現方法は人によってさまざまで、これもclass構文という統一した表現が導入された理由の1つです。

次のコードでは先ほどのclass構文でのクラスを簡略化した関数での1つの実装例です。 この関数でのクラス表現は、継承の仕組みなどは省かれていますが、class構文とよく似ています。

// コンストラクタ関数
const Point = function PointConstructor(x, y) {
    // インスタンスの初期化処理
    this.x = x;
    this.y = y;
};

// `new`演算子でコンストラクタ関数から新しいインスタンスを作成
const point = new Point(3, 4);

大きな違いとして、class構文で定義したクラスは関数と呼び出すことができません。 クラスはnew演算子でインスタンス化して使うものなので、これはクラスの誤用を防ぐ仕様です。 一方、関数でのクラス表現はただの関数なので、当然関数として呼び出せます。

// `class`構文でのクラス
class MyClass {
}
// 関数でのクラス表現
function MyClassLike() {
}
// 関数なので関数として呼び出せる
MyClassLike(); 
// クラスは関数として呼び出すと例外が発生する
MyClass(); // => TypeError: class constructors must be invoked with |new|

このように、class構文で定義したクラスは一種の関数ですが、そのクラスはクラス以外には利用できないようになっています。

糖衣構文. class構文でのみしか実現できない機能はなく、読みやすさや分かりやさのために導入された構文という側面もあるためJavaScriptのclass構文は糖衣構文と呼ばれることがあります。

results matching ""

    No results matching ""