なんでコンストラクター関数のprototypeを使うのか、どんな時に使ったらいいのか忘れがちになりそうなので、JSのコンストラクター関数時に使うprototypeのメモを公開します。
Contents
prototype(プロトタイプ)とは
prototype(プロトタイプ)とは、「オブジェクトに存在する特別なプロパティ」で、「コンストラクター関数と共に使う」ものです。
例えばこんなコンストラクター関数があります。
1 2 3 4 5 6 7 |
function Person(name, age) { this.name = name; this.age = age; } const john = new Person('John', 37); const tom = new Person('Tom', 24); |
このコンストラクター関数にプロトタイプのプロパティを使うためには、コンストラクター関数「Person」に「prototype」を付けてアクセスできます。(この「prototype」は特別なプロパティです。)そして、このプロトタイプにメソッドを追加することができます。
1 2 3 4 5 6 7 8 9 10 11 12 |
function Person(name, age) { this.name = name; this.age = age; } //以下がprototypeを用いてメソッドを追加 Person.prototype.hello = function(){ console.log('hello ' + this.name); } const john= new Person('John', 37); const tom = new Person('Tom', 24); |
上記のようにprototypeを用いることでインスタンス化したオブジェクト(johnやtom)にhelloというメソッドを渡すことができます。
以下の様に、すなわち「john」のオブジェクトにhelloメソッドを実行する「john.hello()」と「hello John」を表示することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.hello = function(){ console.log('hello ' + this.name); } const john = new Person('John', 37); const tom = new Person('Tom', 24); // johnのオブジェクトでhelloメソッドを実行する john.hello(); // 実行結果は「hello John」となる |
prototype(プロトタイプ)の仕組み
- prototypeオブジェクトにメソッドを追加すると、「__proto__」に参照がコピーされる
- この参照をたどることで各インスタンス(johnやtom)で関数(hello)を読み出す仕組みになっている
- 「__proto__」はprototypeの参照なので、「john.__proto__ === tom.__proto__」はtrue(一緒)になる
ちなみにコンストラクター関数内にメソッドをいれるのと、プロトタイプにすることの違いとは?
ふと思ったのだが、コンストラクター関数にメソッドを入れてはいけないかと思った訳だが、結論から言うとプロトタイプの方が処理の面からよさそうということが分かった。例えば、以下の様にコンストラクター関数にメソッドを入れることもできる。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Person(name, age) { this.name = name; this.age = age; // コンストラクター関数にhelloメソッドを入れても結果は同じ this.hello = function(){ console.log('hello '+ this.name); } } const john = new Person('John', 37); const tom = new Person('Tom', 24); john.hello(); |
これでも実行結果は一緒なので、何ら問題はない。
ではなんでプロトタイプを使うのか?というと、プロトタイプとプロトタイプではない場合でメモリ処理に違いがあるからです。
プロトタイプは参照だからメモリの効率化ができる。
プロトタイプはあくまで参照なので、格納されているプロパティは同じ関数を参照しています。一方、プロトタイプを使わない処理は、インスタンスを生成する度(john.hello()やtom.hello()を生成する度)にhelloという関数を追加する必要があるため、プロトタイプに比べてメモリを多く消費することになります。
つまり、効率的にプログラムを動かす場合は、プロトタイプの方が良いという話でした。
おさらいですが、コンストラクター関数からインスタンス化した時には、prototypeの参照が「__proto__」がコピーされます。都度処理を促すコードなのではなく、効率的なメモリ処理にするため、プロトタイプを上手く使いましょう。