継承とプロトタイプとは?

JavaScriptにおける継承とは、あるオブジェクトが別のオブジェクトの機能(プロパティやメソッド)を利用できる仕組みです。これにより、共通の処理をまとめて管理できるため、コードの重複を減らし、保守しやすいプログラムを作成できます。
JavaScriptでは、この継承をプロトタイプチェーン(Prototype Chain)という仕組みで実現しています。オブジェクトに存在しないプロパティやメソッドへアクセスすると、自動的にプロトタイプをたどって探しに行くのが特徴です。
なお、現在のJavaScriptではclass構文を使用して継承を書くことが一般的ですが、その内部では現在もプロトタイプチェーンが利用されています。そのため、プロトタイプの仕組みを理解しておくことは非常に重要です。
プロトタイプチェーンの基本構造
JavaScriptのオブジェクトは、自分自身が持つプロパティやメソッドだけでなく、プロトタイプからも機能を利用できます。
例えば、オブジェクトに存在しないメソッドを呼び出すと、JavaScriptは次のような順番で検索します。
- オブジェクト自身を探す
- プロトタイプ(Prototype)を探す
- さらに上位のプロトタイプを探す
- 最終的に見つからなければ
undefinedとなる
この検索の流れが「プロトタイプチェーン」です。
プロトタイプチェーンの例
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + " makes a sound.";
};
const dog = new Animal("Dog");
console.log(dog.speak());
// Dog makes a sound.
この例では、dog自身にはspeak()メソッドは存在しません。
しかし、JavaScriptは自動的にAnimal.prototypeを検索するため、speak()を呼び出すことができます。
dogにspeak()があるか探す- 見つからないため
Animal.prototypeを探す speak()が見つかるので実行する
プロトタイプチェーンの仕組み
プロトタイプチェーンは次のような流れで機能します。
- オブジェクト自身を検索する
最初に、そのオブジェクト自身にプロパティやメソッドが存在するか確認します。 - プロトタイプを検索する
見つからない場合は、そのオブジェクトのプロトタイプを検索します。 - さらに上位のプロトタイプを検索する
見つからなければ、さらに上位のプロトタイプへと検索を続けます。 - 検索終了
最終的にObject.prototypeまで検索し、それでも見つからなければundefinedとなります。
なお、学習中によく見かける__proto__は内部的なプロトタイプを参照できますが、現在は直接利用することは推奨されていません。プロトタイプを取得する場合は、Object.getPrototypeOf()を利用するのが一般的です。
表:プロトタイプチェーンの流れ
| ステップ | 説明 |
|---|---|
| オブジェクト自身を探す | 最初にオブジェクト自身のプロパティやメソッドを検索します。 |
| プロトタイプを探す | 見つからなければ、そのオブジェクトのプロトタイプを検索します。 |
| さらに上位を探す | 上位のプロトタイプを順番に検索します。 |
| 検索終了 | Object.prototypeまで検索し、それでも存在しなければundefinedになります。 |
プロトタイプ継承のメリット
プロトタイプを利用することで、次のようなメリットがあります。
- コードを再利用できる:共通のメソッドを複数のオブジェクトで共有できます。
- メモリ使用量を削減できる:各インスタンスが同じメソッドを個別に持つ必要がありません。
- 保守しやすい:共通処理を1か所だけ修正すれば済みます。
- class構文でも利用されている:現在主流の
class構文も内部ではプロトタイプチェーンによって実現されています。
プロトタイプチェーンの応用例
次に、継承を利用してAnimalを基にDogを作成する例を紹介します。
現在ではclass構文を使用することが一般的ですが、この例はプロトタイプチェーンの仕組みを理解するためによく使われます。
応用例:プロトタイプ継承によるクラスの拡張
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + " makes a sound.";
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function () {
return this.name + " the " + this.breed + " barks.";
};
const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.speak());
// Buddy the Golden Retriever barks.
このコードでは、DogがAnimalを継承しています。
Animal.call()で親コンストラクタを実行します。Object.create()でプロトタイプを継承します。Dog.prototype.speak()で親メソッドを上書き(オーバーライド)しています。
現在では同じ処理をclassとextendsで記述することが多いですが、その内部では同じプロトタイプチェーンが利用されています。
初心者が知っておきたいポイント
class構文を使っていても、内部ではプロトタイプチェーンが利用されている。- オブジェクトは必要なメソッドを自分自身が持っているとは限らない。
- 存在しないメソッドを呼び出すと、自動的にプロトタイプを検索する。
- JavaScriptの標準オブジェクト(ArrayやDateなど)もプロトタイプチェーンを利用している。
まとめ
- JavaScriptの継承はプロトタイプチェーンによって実現されている。
- オブジェクトに存在しないプロパティやメソッドは、プロトタイプを順番に検索する。
- プロトタイプを利用することで、メソッドを共有し、メモリ効率を向上できる。
- 現在主流の
class構文も、内部ではプロトタイプチェーンを利用している。 - プロトタイプチェーンを理解すると、JavaScriptのオブジェクト指向をより深く理解できる。