こんにちは!開発部の上條です。
今回の記事ではJavaScriptのプロトタイプについてご紹介いたします。なるべくわかりやすく記事にまとめたいと思います。それでは、本稿もどうぞよろしくお願いいたします。
目次はこちら
- 1. prototypeは二種類存在する??
- 2. 関数が持つ prototype (アロー関数を除く)
- 3. オブジェクトが持つ [[Prototype]]
- 4. プロトタイプチェーンとは
- 5. プロトタイプは何が良いのか
prototypeは二種類存在する??

JavaScriptのprototypeと表現されるものには二種類あります。
関数が持つ prototype と
オブジェクトが持つ内部プロパティ [[Prototype]] です。
この二つのprototypeがJavaScriptのプロトタイプの理解を難しくしています。
関数が持つ prototype
このprototypeは直接参照することが出来ます。
試しにObjectコンストラクタが持つprototypeを見てみましょう。
console.log(Object.prototype);

このprototypeは見ての通りオブジェクトです。
このオブジェクトのプロパティやメソッドはjavascriptが予め用意しているものです。
詳細はMDNのObjectから確認出来ます。
ひとまず、関数にはprototypeというオブジェクトがあることが分かれば大丈夫です。
オブジェクトが持つ [[Prototype]]
インスタンスの生成時、新たに生成されたオブジェクトには、
コンストラクタ関数の prototype の参照をもった、内部プロパティ [[Prototype]] が生成されます。
イメージとしてはこんな感じです。
const newObj = new Object();
↓
newObj.[[Prototype]] = Object.prototype;
この [[prototype]] は newObj.[[prototype]] という風に参照することは出来ません。
Object.getPrototypeOf() や __proto__プロパティ(非推奨) を使用して参照することが出来ます。
const newObj = new Object();
console.log(Object.getPrototypeOf(newObj) === Object.prototype); // true
console.log(newObj.__proto__ === Object.prototype); // true
ここから先はわかりやすさ優先で [[prototype]] は __proto__ を使って説明していきます。
プロトタイプチェーンとは
プロトタイプチェーンとはオブジェクトのプロパティを参照しようとした時に、
そのプロパティがなければ、オブジェクト.__proto__にプロパティを探しに行く仕組みです。
更にオブジェクト.__proto__にもプロパティがなければ、オブジェクト.__proto__.__proto__に…
というふうに null になるまで探しに行きます。
例えば
const newObj = {name: '新しいオブジェクト'};
console.log(newObj.toString()); // [object Object]
この例では newObj には toString() メソッドは存在していないはずですが、呼び出すことが出来ます。
これは下のような動きをしています。
newObjにtoString()メソッドが無いか探す。- 見つからなかったので
newObj.__proto__つまりObject.prototypeにtoString()メソッドが無いか探す。 - 見つかった為呼び出す。(newObj.__proto__.toString())
結果 [object Object]
このようにオブジェクトに参照したいプロパティがない場合、コンストラクタのprototypeを探しにいく仕組みをプロトタイプチェーンと言います。
もし最後まで見つからなかった場合は下のようになります。
const newObj = {name: '新しいオブジェクト'};
console.log(newObj.toArray()); // newObj.toArrayis not a function
newObjにtoArray()メソッドが無いか探す。- 見つからなかったので
newObj.__proto__にtoArray()メソッドを探す。 - 見つからなかったので
newObj.__proto__.__proto__に… newObj.__proto__.__proto__は null なので終了。
結果 newObj.toArrayis not a function
prototypeは拡張することが出来ますのであらかじめtoArrayメソッドを用意していれば下のように実行することが出来ます。
Object.prototype.toArray = function() {
return Object.values(this);
}
const newObj = {name: '新しいオブジェクト'};
console.log(newObj.toArray()); // ["新しいオブジェクト"]
今回の例ではビルトインオブジェクトであるObjectオブジェクトのprototypeを拡張しましたが、基本的にはビルトインオブジェクトを触るのは、よくないこととされています。理由は色々ありますので気になった方は調べてみてください。
プロトタイプは何が良いのか
JavaScriptではオブジェクトのインスタンスは全てコピーとなります。
コンストラクタに直接メソッドを追加すると、生成したインスタンスの数だけメソッドが作成され、メモリの消費が大きくなります。
プロトタイプを使用すれば、暗黙的な参照が持てるのでこの問題を解決することが出来ます。
ちなみに、ES2015からはclass構文が用意されています。そちらを使うとprototypeを使わずに継承を行えます。
今回はプロトタイプの説明でしたので詳しく話しませんが、class構文はプロトタイプベース継承の糖衣構文になります。
是非こちらのclass構文も勉強してモダンなコードもかけるようになりましょう!