概要
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を継承していません");
}
}