記事内にはプロモーションが含まれています

【JavaScript】継承の仕組みと実装方法まとめ!class・extends・superの使い方

【JavaScript】継承の仕組みと実装方法まとめ!class・extends・superの使い方 JavaScript

JavaScriptで大規模なアプリケーションを開発したり、効率的なコードを書こうとすると、必ずぶつかるのが「継承」という概念です。

「似たような機能を持つクラスを量産したくない」「既存の機能を少しだけ変えて再利用したい」といった悩みを解決してくれるのが継承ですが、extendssuper といったキーワードの使い方で迷うことも多いでしょう。

かつてのJavaScript(ES5以前)では「プロトタイプ継承」という少し難解な仕組みを理解する必要がありましたが、現在のモダンなJavaScript(ES2015以降)では、class 構文を使うことで、JavaやPHPなどの他言語と同じように直感的に継承を実装できるようになっています。

この記事では、JavaScriptにおける「継承」の基本的な書き方から、メソッドのオーバーライド、コンストラクタでの super の役割、そして多重継承の扱いまで、現在の開発現場で通用する知識をサンプルコード付きで徹底解説します。

【本記事の信頼性】
プロフィール
執筆者:マヒロ
  • 執筆者は元エンジニア
  • SES⇒大手の社内SE⇒独立
  • 現在はこじんまりとしたプログラミングスクールを運営
  • モットーは「利他の精神」

JavaScriptの「継承」とは?なぜ必要なのか

プログラミングにおける「継承(Inheritance)」とは、あるクラス(親クラス)の機能や特徴を、別のクラス(子クラス)が引き継ぐ仕組みのことです。

例えば、「動物(Animal)」というクラスがあるとします。
動物は共通して「食べる」「寝る」という行動をします。

ここから「犬(Dog)」や「猫(Cat)」というクラスを作る時、またゼロから「食べる」「寝る」という機能を書くのは非効率です。

そこで、「動物」クラスを継承して「犬」クラスを作れば、「動物」が持っている機能は自動的に使えるようになります。
あとは、「犬」特有の「ワンと鳴く」機能だけを追加すれば良いのです。

継承を使う主なメリット

  • コードの再利用:共通の機能を親クラスにまとめることで、同じコードを何度も書かずに済みます。
  • 保守性の向上:共通部分の修正は親クラスだけで済むため、バグの修正や機能追加が容易になります。
  • 構造の整理:クラス間の関係性が明確になり、コードの見通しが良くなります。

モダンなclass構文を使った継承の基本(extends)

現在のJavaScriptでは、class キーワードと extends キーワードを使って継承を実装するのが一般的です。
書き方は非常にシンプルです。

基本的な継承の書き方

親クラス(スーパークラス)を定義し、それを extends キーワードを使って子クラス(サブクラス)に継承させます。

// 親クラス(スーパークラス)
class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} はご飯を食べます。`);
  }
}

// 子クラス(サブクラス)
// Animalクラスを継承する
class Dog extends Animal {
  bark() {
    console.log(`${this.name} がワン!と鳴きました。`);
  }
}

// 実行してみる
const pochi = new Dog("ポチ");

pochi.eat();  // 親クラスのメソッドが使える
pochi.bark(); // 子クラス独自のメソッドも使える

実行結果

ポチ はご飯を食べます。
ポチ がワン!と鳴きました。

class Dog extends Animal と記述することで、Dog クラスは Animal クラスを継承しました。

Dog クラス内には eat メソッドが定義されていませんが、親である Animaleat メソッドをそのまま利用できています。

また、Dog クラス独自の bark メソッドも定義し、両方の機能を持つオブジェクトが生成されました。

親クラスの機能を上書きする「オーバーライド」

継承したメソッドを、子クラスで別の処理に書き換えたい場合があります。
これを「オーバーライド(Override)」と呼びます。

やり方は簡単で、子クラスで親クラスと同じ名前のメソッドを定義するだけです。

メソッドのオーバーライド

class Animal {
  speak() {
    console.log("何らかの鳴き声");
  }
}

class Cat extends Animal {
  // 親クラスの speak メソッドを上書き(オーバーライド)
  speak() {
    console.log("ニャー!");
  }
}

const tama = new Cat();
tama.speak();

実行結果

ニャー!

Cat クラスで speak メソッドを再定義しました。

これにより、Cat のインスタンスで speak() を呼び出すと、親クラスの「何らかの鳴き声」ではなく、子クラスで定義した「ニャー!」が実行されます。

これがオーバーライドです。

コンストラクタとsuperキーワードの使い方

継承において最もつまずきやすいのが「コンストラクタ」と super の扱いです。

子クラスで独自の constructor(初期化処理)を書きたい場合、必ず super() を呼び出す必要があります

super() で親クラスのコンストラクタを呼ぶ

super は、親クラス(スーパークラス)を参照するためのキーワードです。
コンストラクタ内で super() と書くことで、親クラスのコンストラクタを実行できます。

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

class Bird extends Animal {
  constructor(name, canFly) {
    // 親クラスのコンストラクタを呼び出す(必須!)
    // これにより this.name が設定される
    super(name);
    
    // 子クラス独自のプロパティ設定
    this.canFly = canFly;
  }

  fly() {
    if (this.canFly) {
      console.log(`${this.name} は空を飛びました!`);
    } else {
      console.log(`${this.name} は飛べません...`);
    }
  }
}

const penguin = new Bird("ペンギン", false);
penguin.fly();

実行結果

ペンギン は飛べません...

子クラス Bird のコンストラクタでは、名前(name)と飛べるかどうか(canFly)を受け取っています。

name の処理は親クラス Animal に任せるため、super(name) を呼び出しています。

重要なルールとして、子クラスのコンストラクタ内で this を使う前に、必ず super() を呼ばなければなりません
これを忘れるとエラー(ReferenceError)になります。

メソッド内で super を使う

コンストラクタだけでなく、通常のメソッド内でも super を使って親クラスのメソッドを呼び出すことができます。

「親クラスの処理を実行したあとに、追加の処理を行いたい」場合などに便利です。

class Robot {
  boot() {
    console.log("システム起動...");
    console.log("チェック完了。");
  }
}

class CleaningRobot extends Robot {
  boot() {
    // 親の boot メソッドを実行
    super.boot();
    // 追加の処理
    console.log("掃除を開始します。");
  }
}

const roomba = new CleaningRobot();
roomba.boot();

実行結果

システム起動...
チェック完了。
掃除を開始します。

CleaningRobotboot メソッド内で super.boot() を記述することで、親クラス Robotboot メソッドの中身を実行しています。

これにより、共通の起動フロー(システム起動など)を書き直すことなく、掃除ロボット独自の「掃除開始」アクションを追加できています。

JavaScriptで多重継承はできる?

「複数のクラスを一度に継承したい(多重継承)」と考えることがあるかもしれませんが、JavaScriptのクラスは「単一継承」のみをサポートしており、多重継承はできません

つまり、extends ClassA, ClassB のような書き方はエラーになります。

ミックスイン(Mixin)による機能の合成

多重継承のようなことを実現したい場合、JavaScriptでは「Mixin(ミックスイン)」というパターンがよく使われます。

これは、特定の機能を持ったオブジェクトやクラスを、別のクラスに混ぜ込む(Mix-in)手法です。

// 泳ぐ機能を提供するMixin
const Swimmable = {
  swim() {
    console.log("スイスイ泳ぎます");
  }
};

// 走る機能を提供するMixin
const Runnable = {
  run() {
    console.log("タッタカ走ります");
  }
};

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

// プロトタイプに機能をコピーして合成する
Object.assign(Human.prototype, Swimmable, Runnable);

const person = new Human("田中");
person.swim();
person.run();

実行結果

スイスイ泳ぎます
タッタカ走ります

Object.assign を使い、Human クラスの prototype に対して、SwimmableRunnable のメソッドをコピーしています。

これにより、Human クラスのインスタンスは、泳ぐことも走ることもできるようになります。

厳密なクラス継承ではありませんが、複数の機能を持たせたい場合に有効なテクニックです。

組み込みオブジェクト(Arrayなど)の継承

現在のJavaScriptでは、配列(Array)や日付(Date)といった組み込みオブジェクトを継承して、独自の拡張クラスを作ることも容易になっています。

配列を継承して便利なメソッドを追加する

// Arrayを継承した独自の配列クラス
class PowerArray extends Array {
  // 配列が空かどうか判定するメソッドを追加
  isEmpty() {
    return this.length === 0;
  }
}

const arr = new PowerArray(1, 2, 3);
console.log(arr.isEmpty()); // false

const emptyArr = new PowerArray();
console.log(emptyArr.isEmpty()); // true

// mapなどの標準メソッドもそのまま使える
const doubled = arr.map(n => n * 2);
console.log(doubled); // PowerArray(3) [2, 4, 6]

extends Array とすることで、標準の配列が持つ全ての機能(push, map, filterなど)を備えた PowerArray を作成しました。

そこに独自の isEmpty メソッドを追加しています。

標準機能を拡張したカスタムコレクションを作りたい場合に非常に強力です。

JavaScriptのスキルを活かして年収を上げる方法

以上、JavaScriptの継承について詳しく解説してきました。

なお、JavaScriptのスキルがある場合には、「副業で稼いで年収を上げる」といったことが可能です。

JavaScriptのスキルを持つ人は多いものの、その分案件数も多く、副業エージェントやフリーランスエージェントを利用することで予想外の高単価案件が見つかることもあります。

エージェントサービスは、登録も利用もすべて完全無料なので、どんな求人や案件があるのか気になる方は、気軽に利用してみるとよいでしょう。