superの概要
| 親クラスへの参照 JavaScript予約語 | ||
|
super 概要 わかりやすく説明
|
||
|
superの基本的な使い方(コンストラクタとメソッドの参照)
extends キーワードで親クラスを継承した子クラスにおいて、super は「関数としての呼び出し」と「プロパティとしてのアクセス」の2通りの役割を担います。
// 親クラス(ベースクラス)の定義
class Animal {
constructor(name) {
this.name = name; // 親クラスの責務として名前を初期化
}
speak() {
console.log(`${this.name}が音を出します。`);
}
}
// 子クラス(派生クラス)の定義
class Dog extends Animal {
constructor(name, breed) {
// 1. 関数としてのsuper: 親クラスのconstructor(name)を実行
super(name);
this.breed = breed; // 子クラス独自のプロパティを初期化
}
speak() {
// 2. プロパティとしてのsuper: 親の本来のspeak()メソッドを拡張実行
super.speak();
console.log(`${this.name}(${this.breed})がワンワンと吠えています!`);
}
}
// インスタンス化の検証
const dog = new Dog("ポチ", "柴犬");
dog.speak();
// 出力結果:
// ポチが音を出します。
// ポチ(柴犬)がワンワンと吠えています!
- 子クラスのコンストラクタでは、独自の
thisにアクセスして値を代入するより前に、まずsuper()を通じて親クラスの初期化フローを完了させなければなりません。 - オーバーライドされた同名メソッドの内部で
super.speak()を呼び出すことで、親の基本動作を流用しつつ、子クラス固有の処理を綺麗に肉付け(差分プログラミング)できます。
superの応用例(ビジネスロジックの拡張と一貫性の維持)
企業データモデルの管理などにおいて、基底クラスが持つベースロジックを一貫して引き継ぎながら、派生オブジェクトに特有の属性を追加していくシーンで super の真価が発揮されます。
// 社員共通のベースクラス
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getDetails() {
return `氏名: ${this.name}, 支給額: ${this.salary}`;
}
}
// マネージャー層に特化した派生クラス
class Manager extends Employee {
constructor(name, salary, department) {
super(name, salary); // 共通プロパティの初期化は親に一任
this.department = department;
}
getDetails() {
// 親クラスが成形した文字列をベースに、管轄部署の情報を合成して出力
return `${super.getDetails()}, 管轄部署: ${this.department}`;
}
}
const manager = new Manager("佐藤", "800万円", "営業部");
console.log(manager.getDetails());
// 出力: 氏名: 佐藤, 支給額: 800万円, 管轄部署: 営業部
注意点
- 子クラスコンストラクタでの「this参照前」のsuper()の絶対強制: 派生クラスのコンストラクタ内で
super()を実行する前にthisにアクセスすると、実行時にエラー(ReferenceError)がスローされます。// 🛑 ReferenceErrorを引き起こすNGコード例 class Cat extends Animal { constructor(name, color) { this.color = color; // ❌ 順序違反!super()を呼ぶ前にthisに触るとクラッシュする super(name); } } - 静的(static)文脈におけるターゲットの自動切り替え:
superはクラス内のメソッドがインスタンスメソッドか静的メソッド(static)かによって、参照先を動的に切り替えます。staticメソッド内でsuperを使用した場合、親クラスの「静的メソッド」へのアクセスとなります。// 静的メソッド文脈でのsuperの挙動 class ParentService { static getEndpoint() { return "https://api.example.com"; } } class ChildService extends ParentService { static getFullUrl() { return `${super.getEndpoint()}/v1/users`; // ⭕ 親の静的メソッドを正確に呼び出す } }
よくある質問
- Q: 親クラスから継承した子クラスで、
constructor(コンストラクタ)の記述自体を完全に省略した場合はどうなりますか?super()を手動で書かなくても平気ですか? - A: 省略しても問題ありません。子クラスにコンストラクタを明示的に記述しなかった場合、JavaScriptエンジンは裏側で自動的に親の引数をそのまま横流しするデフォルトコンストラクタを生成して挿入します。
そのため、子クラス側で独自の初期化処理が一切不要である場合は、コンストラクタの記述自体を丸ごと省くのが一般的です。// コンストラクタを省略しても自動でsuper()が走る例 class SimpleDog extends Animal {} // 裏側で「constructor(...args) { super(...args); }」が自動生成されるため問題なく動く const puppy = new SimpleDog("シロ"); - Q:
superを使って、親クラスよりさらに上の「祖父母クラス」のメソッドを直接指定して呼び出すことは可能ですか? - A: 直接呼び出すことはできません。JavaScriptの
superは、自身のクラスの直上の親([[Prototype]])のみを参照する固定のリンクです。
仮にsuper.super.method()のような連続した記述を試みても、構文エラー(SyntaxError)となります。祖父母クラスのメソッドが親クラスで既にオーバーライドされて消えてしまっている場合、子クラスから親を飛び越えて先祖の処理だけをピンポイントでサルベージすることは、オブジェクト指向のバウンダリを守る仕様上、不可能な設計になっています。
まとめ
superキーワードは、クラスの親子関係におけるプロトタイプ構造を安全に昇り、コンテキストの初期化やメソッドの拡張を精密に行うための言語機構です。
- 子クラスでコンストラクタを独自定義する際には、親の初期化をトリガーする
super()の先頭実行がランタイムエラーを避けるための必須の鉄則となります。 - 親から受け継いだ共通機能を壊すことなく、独自の振る舞いを付け足すオーバーライドのシーンにおいて、
super.method()による差分定義が威力を発揮します。 - インスタンス領域と静的(
static)領域におけるsuperの参照先の挙動の違いを正確に理解し、クラス駆動開発のポータビリティと可読性の向上に役立ててください。