JavaScriptはプロトタイプベースのオブジェクト指向言語です。ここでは、プロトタイプの概要について説明します。
JavaScriptのプロトタイプとは
プロトタイプベースのオブジェクト指向では、新しいオブジェクトは元となるオブジェクトを雛形(=プロトタイプ)として作成されます。
関数はprototypeプロパティを持っている
そして、全ての関数オブジェクトは、prototypeプロパティを持っています。このprototypeプロパティ (以下単にprototypeと記述します)に代入されたオブジェクトは、その関数を元にしたインスタンスから「暗黙の参照」がされます。
参照:JavaScriptのコンストラクタによるインスタンス生成
prototypeの暗黙の参照についてサンプルを交えながら説明します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//コンストラクタ var Person = function(name){ this.name = name; }; //インスタンスを作成 var john = new Person("John"); //prototypeにgreetプロパティを追加 Person.prototype.greet = function(){ alert("Hi, I'm " + this.name); }; john.greet(); //"Hi, I'm John"と表示 |
この時、johnというインスタンスが、greetプロパティを持っているように見えますが、実はコンストラクタのprototypeに追加されたgreetプロパティメソッドを「暗黙の参照」をしているだけで、johnというインスタンス自体はgreetプロパティを持ちません。
なぜprototypeプロパティを使うのか
メソッドの共通化・継承
ひとつはメソッドをインスタンス間で共通化できるからです。インスタンスは、コンストラクタのprototypeのプロパティを参照するので、内容を変更する時もより簡単になります。
1 2 3 4 5 6 7 8 9 10 11 |
var Person = function(name){ this.name = name; }; Person.prototype.greet = function(){console.log("Hi")}; var nick = new Person("Nick"); //メソッドを変更 Person.prototype.greet = function(){console.log("Hello")}; nick.greet();//nickでもHelloと表示される |
また、JavaScriptにおける継承のプロトタイプチェーンに使う時にもprototypeが使われます。
メモリの節約
また、インスタンスを作成すると、コンストラクタ内で定義されたプロパティは生成する全てのインスタンスにコピーされます。しかし、全てのインスタンスで共有する必要がない場合、そのプロパティをコンストラクタで定義するとメモリの無駄遣いとなってしまいます。
そこで、prototypeを使うことで、そのオブジェクトから生成したインスタンスからそのプロパティを参照できメモリの節約となります。
prototypeを使うサンプル
次の例ではコンストラクタを使ってPersonオブジェクトのインスタンスを生成しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//コンストラクタ var Person = function(){ this.sing = function(){ alert("LaLaLa"); }; }; //インスタンスの生成 var singer = new Person(); var writer = new Person(); singer.sing(); //LaLaLa writer.sing(); //LaLaLa |
singerとwriterというインスタンスが作成されていますが、writerにはsingプロパティは必要なく無駄なプロパティを持つことになってしまいます。
prototypeを使った改良
次の例は、上記のプログラムを改良し、singプロパティをPersonオブジェクトのprototypeとして登録しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//コンストラクタ var Person = function(){}; //Personオブジェクトのprototypeに新しいプロパティを追加 Person.prototype.sing = function(){ alert("LaLaLa"); }; //インスタンス生成 var singer = new Person(); var writer = new Person(); singer.sing(); //LaLaLa writer.sing(); //LaLaLa |
singer.sing()では、まずsingerインスタンス内にsingプロパティが登録されているかを調べます。仮にsingというプロパティが存在した場合はPersonオブジェクトのprototypeは参照しません。
インスタンス内に singプロパティが存在しなかった場合、元となるオブジェクトであるPersonオブジェクトのprototypeにsingプロパティが登録されてい るか調べます。
インスタンスでプロパティが書き換えれらた場合
なお、以下のようにsinger.singを書き換えた場合は元のprototypeには影響せず、従ってwriter.singが 変更されることもありません。
singerインスタンスのプロパティとしてsingが新規登録されるため、singer.singは Person.prototype.singを参照する必要がなくなります。
writer.singは(インスタンス内にsingプロパティが登録されていないため)Person.prototype.singを参照したままです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var Person = function(){} Person.prototype.sing = function(){ alert("LaLaLa"); }; var singer = new Person(); var writer = new Person(); singer.sing(); //LaLaLa writer.sing(); //LaLaLa //singer.singを変更してもPerson.prototype.singには影響しない singer.sing = function(){ alert("TuTuTu"); }; singer.sing(); //TuTuTu writer.sing(); //LaLaLa |