JavaScriptのプロトタイプ継承について
Follow @venividivici830
JavaScriptのプロトタイプ継承について、以下にまとめた。
下記の本に詳細に書かれており、非常に参考になった。
パーフェクトJavaScript (PERFECT SERIES 4) 井上 誠一郎 (著), 土江 拓郎 (著), 浜辺 将太 (著)
勉強中のため、間違いがあれば指摘をいただけると幸いである。
下記のJavaScriptの仕様を利用している。
上図により、インスタンスメソッドを定義する下記のコードや、
継承する際の下記のコードについて、理解が深まると思う。
下記のように、プロトタイプ継承を使った方が、メモリー効率、実行効率が良い。
理由は、下記の通り。

newで作られた空オブジェクトにメソッドの領域が確保されるため、newするごとにメソッド定義が複製されてしまい、全てのインスタンスが、同じメソッドの実体を持つことになる。メモリーの使い方が、非効率的である。
対して、プロトタイプ継承を使った方法では、暗黙リンクにより、コンストラクターのprototypeが参照するオブジェクトのプロパティが、全てのインスタンスで共有される。
よって、コンストラクターのprototypeが参照するオブジェクトにメソッドを定義すれば、メソッドの実体は一つであり、メモリー効率、実行効率が良い。

インスタンス. __proto__と、コンストラクター. prototypeは、同じである。
test.js
上記の実行結果は、上に述べてきた内容と一致する。
プロトタイプ継承されるのは、コンストラクター自身のプロパティや、インスタンスのprototypeが参照するオブジェクトのプロパティではなく、コンストラクターのprototypeが参照するオブジェクトのプロパティであることが、確認できると思う。
JavaScriptのプロトタイプ継承について、以下にまとめた。
下記の本に詳細に書かれており、非常に参考になった。
パーフェクトJavaScript (PERFECT SERIES 4) 井上 誠一郎 (著), 土江 拓郎 (著), 浜辺 将太 (著)
勉強中のため、間違いがあれば指摘をいただけると幸いである。
プロトタイプ継承とプロトタイプチェーン
プロトタイプ継承は、下記のように定義できると思う。コンストラクターのprototypeプロパティが参照するオブジェクトのプロパティが、インスタンスに継承される仕組みこれにより、効率的なインスタンスメソッドの実装や、Javaのような継承を実装できる。
下記のJavaScriptの仕様を利用している。
- コンストラクターは、関数である
- 関数は、オブジェクトである
- 全ての関数は、prototypeという名前のプロパティを持つ
- 全てのオブジェクトは、オブジェクト生成に使ったコンストラクターのprototypeプロパティが参照するオブジェクトへの隠しリンク(暗黙リンク)を持つ
オブジェクトのプロパティが読み込まれる時は、プロパティは下記の順で探される。この仕様が、プロトタイプチェーンである。
- オブジェクト自身のプロパティ
- 暗黙リンク先のオブジェクトのプロパティ
- 暗黙リンク先のオブジェクトの、暗黙リンク先のオブジェクトのプロパティ
- これを繰り返し、Object.prototypeが参照するオブジェクトで終わる
上述した内容を図で表すと、下図のようになる。
上図により、インスタンスメソッドを定義する下記のコードや、
var Person = function(name) {
this._name = name;
}
Person.prototype.getName = function() {
return this._name;
}
継承する際の下記のコードについて、理解が深まると思う。
var Dog = function() {}
Dog.prototype = new Animal();
プロトタイプ継承を使ったインスタンスメソッドの実装
インスタンスメソッドは、プロトタイプ継承を使わずに下記のようにも書けるが、
var Person = function(name) {
this._name = name;
this.getName = function() {
return this._name;
}
}
下記のように、プロトタイプ継承を使った方が、メモリー効率、実行効率が良い。
var Person = function(name) {
this._name = name;
}
Person.prototype.getName = function() {
return this._name;
}
理由は、下記の通り。
- 前者は、全てのインスタンスが、同じメソッド定義の実体の複製を持つ。
- 後者は、暗黙リンクにより、コンストラクターのprototypeが参照するオブジェクトのプロパティが、全てのインスタンスで共有される。
以下に補足する。
var Person = function(name) {
this._name = name;
this.getName = function() {
return this._name;
}
}
var person = new Person('Yamada');
上記コードの動きは、下記の通りである。
- newにより、空のオブジェクトが作られる
- newで呼び出されたコンストラクター内では、thisは、作られた空のオブジェクトを参照する
- this._name = nameなどにより、空のオブジェクトにプロパティが設定される
- 変数personに、作られたオブジェクトへの参照が返る
newで作られた空オブジェクトにメソッドの領域が確保されるため、newするごとにメソッド定義が複製されてしまい、全てのインスタンスが、同じメソッドの実体を持つことになる。メモリーの使い方が、非効率的である。
対して、プロトタイプ継承を使った方法では、暗黙リンクにより、コンストラクターのprototypeが参照するオブジェクトのプロパティが、全てのインスタンスで共有される。
よって、コンストラクターのprototypeが参照するオブジェクトにメソッドを定義すれば、メソッドの実体は一つであり、メモリー効率、実行効率が良い。
暗黙リンク先のオブジェクトの取得
暗黙リンク先のオブジェクトは、オブジェクトの__proto__プロパティか、Object.getPrototypeOfメソッドにより取得できる。インスタンス. __proto__と、コンストラクター. prototypeは、同じである。
test.js
var Animal = function() {};
Animal.prototype.breath = function() {};
var Person = function() {};
Person.prototype = new Animal()
Person.prototype.speak = function() {};
var person = new Person();
console.log(person.__proto__ === Person.prototype)
console.log(person.__proto__)
console.log(person.__proto__.__proto__)
console.log(person.__proto__.__proto__.__proto__)
console.log(person.__proto__.__proto__.__proto__.__proto__)
上記の実行結果は、上に述べてきた内容と一致する。
$ node test.js
true
Animal { speak: [Function] }
Animal { breath: [Function] }
{}
null
プロトタイプ継承されるのは、コンストラクター自身のプロパティや、インスタンスのprototypeが参照するオブジェクトのプロパティではなく、コンストラクターのprototypeが参照するオブジェクトのプロパティであることが、確認できると思う。
コメント
コメントを投稿