JavaScript で複数のクラスを拡張する

Waqar Aslam 2023年10月12日
  1. JavaScript で複数のクラスを拡張する合成アプローチ
  2. JavaScript で複数のクラスを拡張するための動作委任アプローチ
JavaScript で複数のクラスを拡張する

JavaScript には、複数のクラスを拡張する組み込みのメカニズムがありません。 ただし、JavaScript で多重継承の同様の効果を得るには、composition および behavioral delegation と呼ばれる手法を使用します。

JavaScript で複数のクラスを拡張する合成アプローチ

コンポジションを使用すると、複数のクラスからプロパティとメソッドを継承する代わりに、各クラスのインスタンスを作成し、それを新しいクラス プロパティとして格納してから、メソッド呼び出しを対応するインスタンスに委任できます。

このアプローチでは、クラスの動作をより細かく制御できることに注意してください。 構成されたクラスのプロパティとメソッドにアクセスし、必要に応じてメソッドをオーバーライドできます。

複数のクラスを拡張する実装

次の例では、クラス C はクラス A とクラス B のインスタンスをプロパティとして持ち、メソッド呼び出しを対応するインスタンスに委譲します。 このようにして、クラス C はクラス A とクラス B の両方のメソッドにアクセスできます。

クラス A には methodA という単一のメソッドがあり、呼び出されると Class A: methodA called というメッセージをコンソールに出力します。 クラス B には methodB という単一のメソッドがあり、呼び出されると Class B: methodB called というメッセージをコンソールに出力します。

class A {
  methodA() {
    console.log('Class A: methodA called');
  }
}

class B {
  methodB() {
    console.log('Class B: methodB called');
  }
}

クラス C は、構成パターンを使用して、多重継承の同様の効果を実現します。 コンストラクターは、クラス A とクラス B のインスタンスを作成し、それぞれプロパティ ab として格納します。

constructor() {
  this.a = new A();
  this.b = new B();
}

クラス C には methodAmethodB の 2つのメソッドがあり、クラス A と B の対応するメソッドを呼び出す前後に追加の操作を実行します。

methodA() {
  console.log('Class C: Additional operation before calling methodA');
  this.a.methodA();
  console.log('Class C: Additional operation after calling methodA');
}

methodB() {
  console.log('Class C: Additional operation before calling methodB');
  this.b.methodB();
  console.log('Class C: Additional operation after calling methodB');
}

最後の 2 行が実行されると、クラス C のインスタンスが作成され、その methodA()methodB(). が呼び出されます。

const c = new C();
c.methodA();
c.methodB();

ソースコード:

class A {
  methodA() {
    console.log('Class A: methodA called');
  }
}

class B {
  methodB() {
    console.log('Class B: methodB called');
  }
}

class C {
  constructor() {
    this.a = new A();
    this.b = new B();
  }

  methodA() {
    console.log("Class C: Additional operation before calling methodA");
    this.a.methodA();
    console.log("Class C: Additional operation after calling methodA");
  }

  methodB() {
    console.log("Class C: Additional operation before calling methodB");
    this.b.methodB();
    console.log("Class C: Additional operation after calling methodB");
  }
}

const c = new C();
c.methodA();
c.methodB();

出力:

合成アプローチ

JavaScript で複数のクラスを拡張するための動作委任アプローチ

このアプローチのもう 1つのバリエーションは、合成クラスのインスタンスを格納する代わりに、メソッド呼び出しを合成クラスのプロパティに直接デリゲートする 動作委譲 を使用することです。

複数のクラスを拡張する実装

クラス C の methodA が呼び出されると、まずコンソールに Class C: methodA を呼び出す前の追加操作 というメッセージを出力し、A.prototype.methodA.call を使用してクラス A の methodA を呼び出します。 (this);、これは Class A: methodA called をコンソールに出力します。

最後に、Class C: methodA 呼び出し後の追加操作というメッセージをコンソールに出力します。

同様に、クラス C の methodB が呼び出されると、最初に Class C: methodB を呼び出す前の追加操作 というメッセージをコンソールに出力し、B.prototype.methodB を使用してクラス B の methodB を呼び出します。 .call(this);、これは Class B: methodB called をコンソールに出力します。

最後に、Class C: methodB 呼び出し後の追加操作というメッセージをコンソールに出力します。

このようにして、クラス C はクラス A とクラス B の両方のメソッドにアクセスし、元のクラスを変更せずにいくつかの操作をメソッドに追加できます。

この場合、クラス C には A と B のインスタンスがないことに注意してください。代わりに、call メソッドを使用して、クラス C のコンテキストで A と B のメソッドを呼び出します。

ソースコード:

class A {
  methodA() {
    console.log('Class A: methodA called');
  }
}

class B {
  methodB() {
    console.log('Class B: methodB called');
  }
}

class C {
  methodA() {
    console.log("Class C: Additional operation before calling methodA");
    A.prototype.methodA.call(this);
    console.log("Class C: Additional operation after calling methodA");
  }

  methodB() {
    console.log("Class C: Additional operation before calling methodB");
    B.prototype.methodB.call(this);
    console.log("Class C: Additional operation after calling methodB");
  }
}

const c = new C();
c.methodA();
c.methodB();

出力:

委任アプローチ

このアプローチは、構成よりもメモリ効率が高くなりますが、構成されたクラスのプロパティにアクセスできないため、柔軟性が低くなります。

ご覧のとおり、どちらの場合も、クラス C は、元のクラスを変更することなく、クラス A とクラス B のメソッドにいくつかの操作を追加できます。 このようにして、元のクラスをクリーンに保ち、分離して、新しいクラス C に特定の機能を追加することができます。

著者: Waqar Aslam
Waqar Aslam avatar Waqar Aslam avatar

I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.

LinkedIn

関連記事 - JavaScript Class