継承と super
このレッスンで分かること
extendsで、既にあるクラスの中身を引き継いだクラスを作れます- 子の
constructorではsuper(...)を呼ぶまでthisを触れません
同じ代入を、2 つのクラスに書くことになる
作品の中に、トップへ大きく出したい種類が混ざるようになったとします。持っているものはほとんど同じで、短い見出しの文字が 1 つ増えるだけです。素直にクラスをもう 1 つ書くと、こうなります。
JavaScript
class Note {
constructor(topic, minutes) {
this.topic = topic;
this.minutes = minutes;
}
render() {
return `${this.topic} ${this.minutes} 分`;
}
}
class PinnedNote {
constructor(topic, minutes, mark) {
this.topic = topic; // 同じ
this.minutes = minutes; // 同じ
this.mark = mark;
}
render() {
return `${this.mark} ${this.topic} ${this.minutes} 分`;
}
}代入も表示の組み立ても二重に書いています。あとで「分」を「分間」に変えたくなったとき、直す場所が 2 か所あることに気づかず、片方だけ直します。
extends を書くと、増えた分だけを書けば済みます。
JavaScript
class PinnedNote extends Note {
constructor(topic, minutes, mark) {
super(topic, minutes);
this.mark = mark;
}
render() {
return `${this.mark} ${super.render()}`;
}
}
console.log(new PinnedNote("正規表現", 40, "重要").render());
// 重要 正規表現 40 分残ったのは、増えた mark と、変わった render だけです。
super は書く場所で意味が変わる
constructor の中の super(...) は、親の constructor を呼びます。これを呼ぶまで、子の constructor では this に触れません。インスタンスの中身は親が作るので、それが終わるまで this がまだ存在しないからです。
JavaScript
class PinnedNote extends Note {
constructor(topic, minutes, mark) {
this.mark = mark; // ReferenceError
super(topic, minutes);
}
}メソッドの中の super.render() は、親クラスの同じ名前のメソッドを呼びます。this.render() ではありません。this.render() と書くと上書きしたほう自身がもう一度呼ばれ、それが止まらなくなって RangeError になります。
増やすプロパティが無く、メソッドだけ変えたいときは、子の
constructorを丸ごと省略できます。省略すると、受け取った引数をそのままsuperに渡すconstructorが自動で用意されます。
継承にするか、プロパティで足すか
種類が分かれて見えても、継承が向かない場面があります。判断の材料は、振る舞いまで違うかどうかです。
| 違いの中身 | 向いている形 |
|---|---|
| 持つデータが 1 つ増えるだけ | プロパティを足す |
| 同じ操作の結果が種類ごとに変わる | 継承してメソッドを上書きする |
| 途中で種類が入れ替わる | 継承にしない |
インスタンスのクラスは、あとから変えられません。注目から通常へ戻ることがあるなら、その都度作り直すことになるので継承は向きません。迷ったらまず 1 つのクラスで書き、同じ条件で分かれる if が 3 本を超えたときに初めて分けます。
よくある間違い
super()を呼ばずにthisを触る —extendsを書いたら、子のconstructorの先頭でsuper(...)を呼びます。引数を渡し忘れると親の側が全部undefinedになり、こちらはエラーにならないのでundefined (undefined)が画面に出てから気づきますsuper.render()をthis.render()と書く — 自分自身を呼び続けてRangeErrorで止まります。親を呼びたいときはsuper.です
要件
- class Work は constructor で title と year を受け取り、describe() で
タイトル (年)を返す - class FeaturedWork extends Work は constructor で title year badge を受け取る。super で親に title と year を渡してから badge を持たせる
- FeaturedWork の describe() は super.describe() の結果の後ろに半角スペースと
[バッジ]を足した文字列を返す - function buildWorkLines(items) を定義する。item.badge が真なら FeaturedWork、そうでなければ Work を作り、describe() の結果を並べた配列を返す
入出力例
buildWorkLines([{"title":"プロフィールサイト","year":2026},{"badge":"注目","title":"作品ギャラリー","year":2026}]) → ["プロフィールサイト (2026)","作品ギャラリー (2026) [注目]"]
buildWorkLines([]) → []
buildWorkLines([{"title":"自己紹介カード","year":2026},{"title":"学習ログ","year":2025}]) → ["自己紹介カード (2026)","学習ログ (2025)"]
buildWorkLines([{"badge":"","title":"スキル一覧の表","year":2026}]) → ["スキル一覧の表 (2026)"]