0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【JavaScript】クラス⑤ 〜継承〜

Posted at

概要

JavaScriptを学習、理解を深めるため「JavaScript Primer 迷わないための入門書」を読み、
理解した内容等を記載していく。

【JavaScript】JavaScript入門一覧」に他の記事をまとめています。

この記事で理解できること

  • クラスの継承とは
  • 継承の方法
  • 継承時の各挙動(コンストラクタ、フィールドなど)

継承

  • 継承とは、クラスの構造や機能を引き継いだ新しいクラスを定義すること。
  • extendsキーワードを使って既存のクラスを継承した新しいクラスを定義できる。
  • 継承されるクラスを親クラス、親クラスを継承するクラスを子クラスと呼んだりする。
// 継承される側
class Parent {
}

// extendsキーワードで親クラス(ここではParent)を継承して新しいクラスを作成
class Child extends Parent {
}

superキーワード

  • 継承した子クラスで親クラスのコンストラクタ(初期化処理)をsuper()とすることで呼び出せる。
// 継承される側
class Parent {
  constructor() {
    console.log("親クラスのコンストラクタ");
  }
}

// extendsキーワードで継承して新しいクラスを作成
class Child extends Parent {
  constructor() {
    super();
    console.log("子クラスのコンストラクタ");
  }
}

const instance = new Child();
// => 親クラスのコンストラクタ
// => 子クラスのコンストラクタ

コンストラクタの処理順

  • class構文では親クラスのコンストラクタ処理を先に行い、その次に子クラスのコンストラクタ処理を行う必要がある。
  • 子クラスのコンストラクタで、thisを触る前に親クラスのコンストラクタ処理super()を呼び出さないとReferenceErrorとなる。
class Parent {
  constructor() {
    this.name = "Parent";
  }
}

class Child extends Parent {
  constructor() {
    // super()をコメントアウトして試すとReferenceErrorになる
    super();
    this.name = "Child";
    console.log(this.name);
  }
}

const instance = new Child(); // => Child

クラスフィールドの継承

  • Publicクラスフィールドもコンストラクタの処理順と同じく、親クラスのフィールドが初期化された後に子クラスのフィールドが初期化される。
  • 親クラスで定義されていたフィールドも、実際にインスタンス化したオブジェクトのプロティとして定義される。
  • Privateクラスフィールドは、親クラスで定義したフィールドは子クラスに定義されない(親クラス内でのみ参照可能)。
  • JavaScriptでは、クラスの外に公開したくないが、子クラスからは利用できるようにしたいというような中間の制限を持ったプロパティを定義する構文はない。
class Parent {
  parentName = "Parent";
  #privateParent = "親クラス内でのみ参照できる"; 
}

class Child extends Parent {
  childName = "Child";
}

const instance = new Child();

console.log(instance.parentName);      // => Parent
console.log(instance.childName);       // => Child
console.log(instance.#privateParent);  // => SyntaxError: Private field '#privateParent' must be declared in an enclosing class

プロトタイプ継承

  • 子クラスのインスタンスから親クラスのプロトタイプメソッドもプロトタイプチェーンの仕組みによって呼び出せる。
  • class構文ではプロトタイプオブジェクトを参照する仕組みによって継承が行われ、この継承の仕組みをプロトタイプ継承と呼ぶ。
  • 静的メソッドも同様に継承される。
class Parent {
  parentName = "Parent";
  
  parentMethod() {
    console.log("親クラスのプロトタイプメソッド");
  }
}

class Child extends Parent {
  childName = "Child";
}

const instance = new Child();

instance.parentMethod(); // => 親クラスのプロトタイプメソッド
/**
 * parentMethodメソッドが呼ばれるまでの流れ
 * ①インスタンス(instance)内を探す       -> 存在しない
 * ②Child.prototype内部プロパティを探す  -> 存在しない
 * ③Parent.prototype内部プロパティを探す -> 存在する
 */

superプロパティ

  • 子クラスのプロトタイプメソッドからは、super.プロパティ名で親クラスのプロトタイプメソッドを参照できる。
  • 子クラスから親クラスのプロトタイプメソッドへのアクセスはsuper.プロパティ(メソッド)名で参照できる。
class Parent {
    method() {
        console.log("親クラスのメソッド");
    }
}

class Child extends Parent {
    method() {
        console.log("子クラスのメソッド");
        super.method();
    }
}

const child = new Child();
child.method();
// => 子クラスのメソッド
// => 親クラスのメソッド

継承の判定

  • あるクラスが指定したクラスをプロトタイプ継承しているかはinstanceof演算子を使って判定できる。
class Parent {}
class Child extends Parent {}
class OtherChild {}

const child = new Child();
const otherChild = new OtherChild();

extendParent(child);      // => Parentを継承しています
extendParent(otherChild); // => Parentを継承していません

function extendParent(instance) {
  if (instance instanceof Parent) {
    console.log("Parentを継承しています");
  } else {
    console.log("Parentを継承していません");
  }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?