JavaScriptのクラスでは、インスタンスメソッドやstaticメソッド、アクセッサプロパティなどを用いてクラスの動作や機能を定義することができます。
しかし、全く0の状態からいくつものクラスを作成するのではなく、あるクラスの内容をそのまま再利用して新しいクラスを作りたいという場合もあるでしょう。

そこで今回は、クラスと継承について解説していきます。

クラスの継承

クラスの継承とは、あるクラスの内容を引き継いで新しいクラスを作成することです。
内容を引き継ぐ側を親クラスと呼び、引き継ぎを受けて新しいクラスを作成する側を子クラスと呼びます。

クラスの継承には、extendsキーワードを使います。

class Childclass extends Parentclass {}

例えば、シェフというクラスの内容を、中華料理専門のシェフに継承すると以下のようになります。

class Chef {
  constructor(name, experty) {
    this.name = name;
    this.experty = experty;
  }
  info() {
    console.log(`${this.name}さんは、${this.experty}のシェフです`);
  }
}

class ChineseChef extends Chef {}
const chinesechef = new ChineseChef('王', '中華料理');
chinesechef.info();
// "王さんは、中華料理のシェフです"

ChineseChefクラスは、すでに定義されているChefクラスのコンストラクタのデフォルトを使用しているので、短いコードでプロパティやメソッドを使用することができます。

子クラスにメソッドを追加

親クラスから継承した後に、子クラスだけに使用したいメソッドを追加することができます。

class Chef {
  constructor(name, experty) {
    this.name = name;
    this.experty = experty;
  }
  info() {
    console.log(`${this.name}さんは、${this.experty}のシェフです`);
  }
}

class ChineseChef extends Chef {
  // 新しくメソッドを定義
  experience() {
    console.log(`${this.name}さんは、${this.experty}店を開業して20年です`);
  }
}
const chinesechef = new ChineseChef('王', '中華料理');
chinesechef.info();
chinesechef.experience();

実行結果

// "王さんは、中華料理のシェフです"
// "王さんは、中華料理店を開業して20年です"

experienceメソッドの追加によって、ChineseChefクラスだけに使用する新たな情報を出力することができました。

このようにクラスの継承を利用すると、親クラスから拾える内容はできるだけ使い回し、子クラスだけが持つ内容を追加することができます。

オーバーライド

オーバーライドとは、引き継がれた内容を、独自のものに上書きすることです。
クラスは継承するだけでなく、superというキーワードを用いて、親クラスのコンストラクタやメソッドの内容を上書きすることができます。

子クラスの中で、コンストラクタやメソッドの1行目にsuperを記述します。

// 親クラス
class Parentclass {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  method() {
  }
}

// クラスの継承(子クラス)
class Childclass extends Parentclass {
  constructor(x, y) {
    // 親クラスのコンストラクタの呼び出し
    super(x, y);
  }
  method() {
    // 親クラスのメソッドの呼び出し
    super.method();
  }
}

以下は、シェフという親クラスを元に、日本料理のシェフという子クラスを作成しています。
親クラスで定義しているプロパティやメソッドを呼び出し、再定義をします。
superのキーワードに注目してください。

class Chef {
  constructor(props) {
    this.name = props.name;
    this.experty = props.experty;
    this.position = props.position;
  }
  intro() {
    console.log(`${this.name}さんは、${this.experty}の${this.position}です`);
  }
}

class JapaneseChef extends Chef {
  // 子クラスのconstructorを再定義
  constructor(props) {
    super(props); // 親クラスのpropsの呼び出し
    this.repertoire = props.repertoire || []; // repertoireを追加
  }
  menu() {
    console.log(`${this.name}さんのおすすめメニューは、${this.repertoire}です`);
  }
  // 子クラスのintroメソッドを再定義
  intro() {
    super.intro(); // 親クラスのintroメソッドの呼び出し
    this.menu(); // menuメソッドを呼び出し
  }
}

const japanesechef = new JapaneseChef({
  name: '佐藤毅',
  experty: '日本料理',
  position: '人気シェフ',
  repertoire: ['そば', '刺身定食', 'カツ丼']
});
japanesechef.intro();

実行結果

// "佐藤毅さんは、日本料理の人気シェフです"
// "佐藤毅さんのおすすめメニューは、そば,刺身定食,カツ丼です"

親クラスのChefには、名前、専門、ポジション(name, experty, position)というプロパティと、シェフを紹介するためのメソッド(intro)が用意されています。
ここまでが基準となる親クラスで定義されているものです。

子クラスのJapaneseChefでは、親クラスのプロパティをsuper(props)で呼び出した後、子クラスだけが持つthis.repertoireを追加しています。
また、親クラスのメソッドをsuper.intro()で呼び出した後に、子クラスだけが持つmenu()メソッドを追加しているもの確認できます。

これで、"佐藤毅さんは、日本料理の人気シェフです“という自己紹介と、"佐藤毅さんのおすすめメニューは、そば,刺身定食,カツ丼です“というメニューのレパートリーの情報を出力することができました。

まとめ

今回は、クラスの継承方法について解説しました。

元の型を継承することができれば、短いコードで新しいクラスを作成することができるため、可読性が上がります。
また、継承を利用することで共通の内容をできる限り1箇所にまとめることができ、メンテナンスの面にも貢献できます。

これらの内容を踏まえ、クラス構文を使ってプログラムを書く際に、クラスの継承も取り入れてみてください。

合わせて読みたいクラスシリーズ

第1回:オブジェクト指向とクラス
第2回:クラスとメソッド -インスタンスメソッド・アクセッサプロパティ
第3回:クラスとメソッド -staticメソッド
第4回:クラスの継承(当記事)