継承とは、クラス同士の親子関係を作る仕組みです。 子クラス(_サブクラス_と呼ばれることもあります)は、親クラス(_スーパークラス_と呼ばれることもあります)で定義された振る舞いとデータを利用できます。
class Pet {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`This is my pet, ${this.name}.`);
}
}
class Dog extends Pet {}
const dog = new Dog('Otis');
dog.introduce();
// => This is my pet, Otis.
子クラスの宣言にあるextendsキーワードは、プロトタイプチェーンを通じて親クラスとの関係を築きます。
子クラスのコンストラクターが作るオブジェクトは、プロトタイプチェーンに親クラスのプロトタイプを持ち、親クラスで定義されたメソッドやデータにアクセスできます。
const dog = new Dog('Otis');
Dog.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(dog); // => true
Pet.prototype.isPrototypeOf(Dog.prototype); // => true
Pet.prototype.hasOwnProperty('introduce'); // => true
Dog.prototype.hasOwnProperty('introduce'); // => false
dog.hasOwnProperty('introduce'); // => false
子クラスがコンストラクター関数を定義していない場合は、親のコンストラクター関数が使われます。
しかし、子クラスが独自のコンストラクター関数を定義している場合は、親のコンストラクターを明示的に呼び出す必要があります。
子クラスのコンストラクターのスコープから親のコンストラクターを呼び出すには、superキーワードを使います。
class Pet {
constructor(name) {
this.name = name;
}
}
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const dog = new Dog('Otis', 'Pug');
親のコンストラクターは、新しいオブジェクトを初期化してthisに代入する役割を担っているため、子クラスのコンストラクターがthisを使う前に呼び出さなければなりません。
class Dog extends Pet {
constructor(name, breed) {
// using 'this' before calling the parent constructor with 'super'
this.breed = breed;
super(name);
}
}
const dog = new Dog('Otis', 'Pug');
// => ReferenceError: Must call super constructor in derived class before accessing 'this'...
子クラスは、親から継承した振る舞いに加えて、独自の振る舞いを定義できます。
これが継承を使う主な理由のひとつです。親クラスが提供する共通のメソッドとデータを通じてつながりながら、それぞれ独自のデータとメソッドを持つ特化した子クラスを作ることができます。
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
describe() {
console.log(`${this.name} is a ${this.breed}.`);
}
}
const dog = new Dog('Otis', 'Pug');
dog.introduce();
dog.describe();
// => 'This is my pet, Otis.'
// => 'Otis is a Pug.'
子クラスは、親クラスで定義されたメソッドの振る舞いをオーバーライドし、子クラスで定義した振る舞いで置き換えたり拡張したりすることもできます。
class Cat extends Pet {
// replacing parent class behavior
introduce() {
console.log(`This is my cat, ${this.name}.`);
}
}
class Dog extends Pet {
constructor(name, breed) {
super(name);
this.breed = breed;
}
describe() {
console.log(`${this.name} is a ${this.breed}.`);
}
// extending parent class behavior
introduce() {
super.introduce();
this.describe();
}
}
const cat = new Cat('Milo');
cat.introduce();
// => 'This is my cat, Milo.'
const dog = new Dog('Otis', 'Pug');
dog.introduce();
// => This is my pet, Otis.
// => Otis is a Pug.
子クラスにある同じ名前のメソッドの本体から、親クラスで定義されたメソッドを呼び出すには、superキーワードで親クラスを参照する必要があります。