1. TOPTOP
  2. Webサービス
  3. JavaScript

JavaScript(ECMAScript2015) クラス構文の継承では子クラスのコンストラクタが親クラスのコンストラクタに上書きされる

js_es6_class_extends_constructor_0

JavaScript(ECMAScript2015)のクラス構文では、クラスの継承が使えます。ですがクラスの継承では子クラスのコンストラクタは親クラスのコンストラクタを上書きするため、不用意に使うと意図せざる結果が出力されます。

親クラスを継承するときのコンストラクタの使い方

ロールプレイングゲームを想定して、「勇者」のHPとMPを確認するコードで、親クラスのコンストラクタが子クラスのコンストラクタに上書きされる例を紹介します。詳しいコードについてリンク先のGitHubのページを参考にしてください。

js_es6_class_extends_constructor_1

上記の画像のうち、1行目はDragonFantasyクラスのshowメソッドを使って、name,job,hpの各プロパティを表示させています。またDragonFantasyクラスを継承したDragonFantasyMpは、job,mpの各プロパティを表示させています(GitHub(新しいタブで開く))。

constructor(name, job, hp, mp) {
   super(name, job, hp);
   this.mp = mp;
}

ここで注目すべきはコンストラタの引数とsuperキーワードの引数です。親クラスから継承する子クラスのコンストラクタは、親クラスの仮引数に加えて、子クラスで新しく初期化するプロパティも仮引数に取る必要があります。

またsuperは子クラスが親クラスのコンストラクタを継承をするときに必須のキーワードですが、親クラスのプロパティを仮引数に取る必要があります。

つまり正しく子クラスのコンストラクタを使うためには、親クラスのコンストラクタで定義したプロパティに、子クラスのコンストラクタで新しいプロパティを追加する必要があります。

親クラスの仮引数を継承していない場合

一方、子クラスで定義するコンストラクタは、親クラスのコンストラクタで使われている仮引数を記述しなくても良いと勘違いすることがあります。

constructor(mp){    
    super();
    this.mp = mp;
}

その場合、子クラスのコンストラクタは、このような書き方になります。この記述では親クラスのプロパティが、constructor()でもsuperキーワードも使われていません。したがってmpプロパティのみが定義されたコンストラクタが新しく出来上がってしまいます(GitHub(新しいタブで開く))

js_es6_class_extends_constructor_2

するとjobプロパティは未定義の変数として認識されてしまいます。またmpプロパティは親クラスのnameプロパティ名を上書きし、DragonFantasyMpクラスをインスタンス化した時に第一引数とした’Tom’がプロパティ値として認識されてしまいます。

新しいプロパティのみを引数にする場合

もし子クラスのコンストラクタで、新しいプロパティのみを引数とする場合、コンストラクタを以下のように定義します。

constructor(mp){    // mpのみを初期化する場合
    super('Tom', '勇者', 100);
    this.mp = mp;
}

この場合、superキーワードで親クラスのコンストラクタで定義したプロパティの値を直接、実引数にします。

const heromp = new DragonFantasyMp(0);

さらに継承したクラスをインスタンス化するときに使う実引数は、子クラスのコンストラクタで定義した仮引数を対象にします(GitHub(新しいタブで開く))。