JavaScriptのプロトタイプを楽器のギターに例えると、あるギターのプロトタイプは「そのギターが持つ特定の機能や特性を定義するような設計図」のようなものです。
具体的にどういうことかと言うと、例えばギターの形状、音色、使用材料などの情報がプロトタイプに含まれます。「ストラトキャスター」というモデル名で作られている場合、そのギターのプロトタイプには「ストラトキャスター」というモデル名や、ボディスタイル、3つのシングルコイルピックアップ、トレモロユニット、ネック形状などが含まれます。
同じプロトタイプを共有する別のギターが作られると、そのギターも同じ特性を持つことになります。「新しいストラトキャスターを作る」という場合、ある程度同じ特徴が引き継がれることになります。
同様に、JavaScriptのオブジェクトには、それぞれのプロトタイプが存在し、オブジェクトが持つ機能や特性は、そのプロトタイプによって決定されます。あるオブジェクトが別のオブジェクトを継承する場合、前者のオブジェクトのプロトタイプは後者のプロトタイプになり、前者のオブジェクトは後者のオブジェクトと同じ特性を持つことになります。(オブジェクトについては『JavaScript オブジェクトとは何か?概要と記述方法を基礎から解説』を参照してください。)
プロトタイプの基本
オブジェクトのプロトタイプは、次のようにして設定することが出来ます。
function Guitar(manufactureYear, color) {
this.manufactureYear = manufactureYear;
this.color = color;
}
Guitar.prototype.getCondition = function() {
return this.manufactureYear + "年につくられた" + this.color + "色です";
};
let guitar01 = new Guitar("1976", "ビンテージホワイト");
console.log(guitar01.getCondition()); // "1976年につくられたビンテージホワイト色です"
上記の例では、Guitar
関数は、manufactureYear
とcolor
のプロパティを持つオブジェクトを作成するコンストラクタ関数です。その後、getCondition
というメソッドがGuitar.prototype
に追加されています。これにより、Guitar
オブジェクトのすべてのインスタンスでgetCondition
メソッドを使用出来ます。
プロトタイプは、JavaScriptのオブジェクト指向プログラミングにおいて非常に重要な役割を果たします。オブジェクトを簡単に拡張出来、効率的なコードの再利用が実現可能になります。
プロトタイプを使うメリット
JavaScriptのプロトタイプを使うメリットは、同じ種類のオブジェクトを多数作成する場合に特に役立ちます。Webアプリケーションを作成する際、同じ機能を複数のオブジェクトで共有する必要があるときはプロトタイプを使うと効果的です。
プロトタイプを使えば、同じ機能を共有するために重複するコードを回避出来、コードの可読性や保守性が向上します。また、オブジェクト同士の継承関係を明確にすることで、コードの再利用性も高まります。コードの品質を向上させ開発の効率性を高めることが可能になるメリットがあります。
以下にプロトタイプのメリットを実例を交えてご紹介します。
メモリの効率化
プロトタイプを使用することで、同じプロパティやメソッドを複数のオブジェクトで共有することが出来ます。これにより、同じプロパティやメソッドを複数のオブジェクトに持たせる必要がなく、メモリを効率的に使用することが可能です。
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log("私は" + this.name + "です。" + this.age + "歳です。");
}
}
let person1 = new Person("Konan", 7);
let person2 = new Person("Ran", 16);
これら2つ(person1
, person2
)のオブジェクトは、それぞれname
、age
、greet
という同じプロパティとメソッドを持っています。ですがこれらはオブジェクトそれぞれに定義してしまっています。この場合、同じプロパティとメソッドを持つオブジェクトを持つにも関わらず、メモリを大量に消費してしまいます。(person3
, person4
, … person999
など)
一方、プロトタイプを使用すると、同じプロパティとメソッドを複数のオブジェクトで共有できます。以下は、Person
コンストラクタのプロトタイプを使用した例です。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log("私は" + this.name + "です。" + this.age + "歳です。");
}
let person1 = new Person("Konan", 7);
let person2 = new Person("Ran", 16);
この例では、Person
コンストラクタでオブジェクトを作成する際、greet()
メソッドはプロトタイプから継承されます。これにより、同じプロパティとメソッドを持つオブジェクトを複数作成しても、メモリの使用を節約することが出来ます。
継承
プロトタイプを使用することで、オブジェクトの継承を実現することができます。これにより、継承元のオブジェクトのプロパティやメソッドを継承したオブジェクトにも使用することができます。
これは詳細に説明すると非常に長くなるので継承のサンプルをさらっと見ておきましょう。別記事で解説します。
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(this.name + " is eating.");
}
function Bird(name) {
Animal.call(this, name);
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
Bird.prototype.fly = function() {
console.log(this.name + " is flying.");
}
let animal = new Animal("Tiger");
animal.eat(); // "Tiger is eating."
let bird = new Bird("Eagle");
bird.eat(); // "Eagle is eating."
bird.fly(); // "Eagle is flying."
この例では、Bird
コンストラクタでAnimal
オブジェクトを継承しています。Animal
のプロトタイプをBird
のプロトタイプに設定することで、Animal
のメソッドであるeat
をBird
でも使用することができます。また、Bird
独自のメソッドであるfly
を定義することもできます。
プロトタイプを使用することでオブジェクトの継承を実現することが出来ます。オブジェクトの再利用性が高まり、コードの保守性が向上します。
ライブラリの拡張
以下は、JavaScriptのプロトタイプを使って、配列をシャッフルするためのメソッドを拡張する例です。配列の要素をランダムに並び替えます。
// Arrayオブジェクトのprototypeを拡張して、shuffleメソッドを追加する
Array.prototype.shuffle = function() {
let array = this.slice(); // 配列のコピーを作成
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
// 使用例
let array = [1, 2, 3, 4, 5];
let shuffledArray = array.shuffle();
console.log(shuffledArray); // [4, 2, 5, 1, 3] (ランダムな順序で並び替えられた配列)
Arrayオブジェクトのprototypeを拡張することで、既存の配列オブジェクトに対して、新しいshuffleメソッドを追加しています。これにより、任意の配列をarray型をshuffle()
メソッドでシャッフルすることが出来ます。
プロトタイプチェーン
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(this.name + " is eating.");
}
function Bird(name) {
this.name = name;
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
Bird.prototype.fly = function() {
console.log(this.name + " is flying.");
}
let bird = new Bird("Eagle");
bird.eat(); // "Eagle is eating."
この例では、Bird
オブジェクトのeat
メソッドが実行されています。しかし、Bird
オブジェクトにはeat
メソッドが定義されていません。そのため、プロトタイプチェーンによって、Bird
オブジェクトのプロトタイプであるAnimal
オブジェクトのeat
メソッドが呼び出されます。
このように、プロトタイプチェーンを利用することで、オブジェクトの継承やメソッドの再利用が容易になります。
まとめ
JavaScriptのプロトタイプを使用することで、オブジェクトの再利用性や保守性を高めることができます。メモリの節約やオブジェクトの継承、ライブラリの機能拡張、プロトタイプチェーンを利用したメソッドの再利用が挙げられます。プロトタイプはJavaScriptにおいて重要な概念ですので、理解しておくことをおすすめします。