JavaScriptのプロトタイプ継承について



JavaScriptのプロトタイプ継承について、以下にまとめた。

下記の本に詳細に書かれており、非常に参考になった。

パーフェクトJavaScript (PERFECT SERIES 4) 井上 誠一郎  (著), 土江 拓郎 (著), 浜辺 将太 (著)

勉強中のため、間違いがあれば指摘をいただけると幸いである。

プロトタイプ継承とプロトタイプチェーン

プロトタイプ継承は、下記のように定義できると思う。
コンストラクターのprototypeプロパティが参照するオブジェクトのプロパティが、インスタンスに継承される仕組み
これにより、効率的なインスタンスメソッドの実装や、Javaのような継承を実装できる。

下記のJavaScriptの仕様を利用している。
  • コンストラクターは、関数である
  • 関数は、オブジェクトである
  • 全ての関数は、prototypeという名前のプロパティを持つ
  • 全てのオブジェクトは、オブジェクト生成に使ったコンストラクターのprototypeプロパティが参照するオブジェクトへの隠しリンク(暗黙リンク)を持つ
オブジェクトのプロパティが読み込まれる時は、プロパティは下記の順で探される。この仕様が、プロトタイプチェーンである。
  1. オブジェクト自身のプロパティ
  2. 暗黙リンク先のオブジェクトのプロパティ
  3. 暗黙リンク先のオブジェクトの、暗黙リンク先のオブジェクトのプロパティ
  4. これを繰り返し、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');

上記コードの動きは、下記の通りである。

  1. newにより、空のオブジェクトが作られる
  2. newで呼び出されたコンストラクター内では、thisは、作られた空のオブジェクトを参照する
  3. this._name = nameなどにより、空のオブジェクトにプロパティが設定される
  4. 変数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が参照するオブジェクトのプロパティであることが、確認できると思う。

コメント

このブログの人気の投稿

PowerShell 6で、Shift_JISのCSVをImport-Csvで読み込んだら文字化けした

Windowsで、特定のユーザーに特定のサービスの再起動を許可する

PowerShellでイベントログを取得する時、「指定した選択条件に一致するイベントが見つかりませんでした。」が煩わしいのでcatchする