`
rensanning
  • 浏览: 3512425 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:37452
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:604194
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:677862
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:87184
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:399731
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69037
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:90417
社区版块
存档分类
最新评论

【转】图解JavaScript的原型链(Prototype Chain)

 
阅读更多

JavaScriptのプロトタイプチェーンについて理解しようとしたのだけど、prototypeとか__proto__とかごちゃごちゃになって、色んなブログを読んでもなかなか理解しきれなくて悶々としていたのだが、図を書いたらパッと理解できた!以下、情報ソースはなるべくECMAScript仕様書(3rd)を元にするようにして書きました

なぜ分かりづらいのか?

そもそも、なぜJavaScriptのプロトタイプチェーンは自分にとってこうも分かりづらかったのだろうか?自分なりに分析してみると、まず、「似ているが違う用語が沢山ある」という点がある。ざっとあげただけでも、「prototypeと__proto__」「__proto__と[[Prototype]]」「FunctionとFunctionオブジェクト」などがある。そして次に、「入り組んだ構造が動的に変化する」という点がある。上記のように似たような用語が沢山出てくる上に、それらの構造が入り組んでおり、しかもそれらが動的に変化していくのだ。これは自分の脳みそではきつい。さらに、__proto__プロパティ処理系によっては実装されていない(ChromeFirefoxならおk)など「環境依存」の部分もある。自分が中々理解できなかったのはこういった要素に原因があるのではと思った。

図にすれば理解しやすくなる!

なので、上記を解決するために「図」を用いることにした。それにより「用語が明確に区別され」「構造を状態ごとに追う事ができる」と思ったからだ。ということで、JavaScriptのオブジェクト構造がどのようになるかを図で可視化していく。図は以下のように「丸はオブジェクト。矢印はプロパティ」というシンプルな図を用いることにする。

f:id:maeharin:20130215001056j:image

サンプルコード

以下のサンプルコードを実行した時のオブジェクト構造を図にする

var C = function (name) {
    this.name = name;
};

C.prototype.x ='xxx';

var c1 = new C('hoge');
var c2 = new C('fuge');

C.prototype.y = 'yyy';

C.prototype = {z: 'zzz'};
var c3 = new C('piyo');

 

関数の定義

まずはこの部分からみていく

var C = function (name) {
    this.name = name;
};

 

唯一のオブジェクト「グローバルオブジェクト」

・まず、JavaScriptには唯一のオブジェクト、グローバルオブジェクトが存在する
・グローバルコンテキスト変数を定義すれば、グローバルオブジェクトのプロパティになる
・上記のコードでは、グローバルコンテキストで変数Cにfunction式で生成したオブジェクトをセットしている
・つまり、グローバルオブジェクトのプロパティCにオブジェクトをセットしていることになる
f:id:maeharin:20130214141736j:image

function式で生成されるオブジェクトは「Function」から生成された「Functionオブジェクト」

・では、function式で生成されるオブジェクトとはどのようなオブジェクトだろうか?
・まず前提として、ここではfunction式でオブジェクトを生成しているが、それ以外にもFunctionコンストラクタとfunction文を使ってもほぼ同様のことができる
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Statements/function
・で、function式を実行すると組み込みのFunctionというオブジェクトを元に、新たなオブジェクトが生成される
・Functionを元に生成されたオブジェクトを総称してここでは「Functionオブジェクト」と呼ぶことにする(ECMAScript仕様書(3rd )では、「Function Object」と呼ばれている)
・今回のサンプルにおいて、Cは「Functionオブジェクト」である
f:id:maeharin:20130214141737j:image

全ての「Functionオブジェクト」はprototypeプロパティを持つ

・では、Functionオブジェクトとはどのようなオブジェクトだろうか?
・まず、全てのFunctionオブジェクトはprototypeというプロパティを持つ
・そしてprototypeプロパティの先には通常何らかのオブジェクトがセットされている
・実はFunctionも自身から生成されたFunctionオブジェクトである
・なので、FunctionもCもprototypeプロパティをもつ
f:id:maeharin:20130214141738j:image

全ての「オブジェクト」は__proto__プロパティを持つ

・次にオブジェクト全般について突っ込んでみていく
・まず、全ての「オブジェクト」は内部プロパティ[[Prototype]]を持つ。これはとても重要なポイント。

Internal properties and methods are not part of the language. They are defined by this specification purely for expository purposes. (略)Native ECMAScript objects have an internal property called [[Prototype]]. 

ECMAScriptの仕様書(3rd )

・[[Prototype]]は仕様上、内部プロパティとされているが実際に__proto__というプロパティ名でプログラムから扱える実装が多い(ChromeやFirefox)
・ただし、__proto__が存在しない実装もあるので注意
・ここでは[[Prototype]]を__proto__という呼び名で統一することにする
f:id:maeharin:20130214141739j:image

Functionオブジェクト.__proto__は、Function.prototype

・__proto__プロパティの参照先を詳しくみていく
・まず、「Functionオブジェクト(ここではC)」の__proto__プロパティは、Function.prototypeを参照する
f:id:maeharin:20130214141740j:image

__proto__があると何が嬉しいのか?

・自分自身に存在しないプロパティ/メソッドが呼び出された時、__proto__を辿った先にあるオブジェクトにそのプロパティ/メソッドが無いか探し、あればそれを使う
・例えば、Function.prototypeにはtoStringメソッドが存在する。なので、以下のようにするとtoStringが呼び出せる
・C自身はtoStringメソッドを持っていないが、__proto__を辿った先にあるFunction.prototypeがtoStringメソッドを持っているので、それが呼び出される。

C.toString();
//"function (name) {                                                                                                                                                       
//    this.name = name;
//}"

f:id:maeharin:20130214141741j:image

検証:
(hasOwnPropertyメソッドは、オブジェクトが指定されたプロパティを持っているかどうかを示す真偽値を返す)

C.hasOwnProperty('toString'); //false
Function.prototype.hasOwnProperty('toString'); //true

 

Functionオブジェクト.__proto__.__proto__は、Object.prototype

・さきほど全てのオブジェクトは__proto__プロパティを持つと述べたが、では、Functionオブジェクトの__proto__はどこまで繋がるのだろうか?
・まず、Functionオブジェクト.__proto__.__proto__は、Object.prototypeである。実はObjectも「Functionオブジェクト(Functionから生成されたオブジェクト)」である。そのため、Objectもprototypeプロパティを持っているのだ
・さらに駆け上り、Object.prototype.__proto__の先は何かというと、それはnullになってそこで終わっている
・Object.prototypeにはhasOwnPropertyメソッドがある。なので、以下のようにするとチェーンを辿ってメソッドが実行できる

C.hasOwnProperty();

f:id:maeharin:20130214141742j:image

検証:
hasOwnPropertyメソッドはObject.prototypeのメソッドである

C.hasOwnProperty('hasOwnProperty'); //false
C.__proto__.hasOwnProperty('hasOwnProperty'); //false
C.__proto__.__proto__.hasOwnProperty('hasOwnProperty'); //true

 

ObjectとFunctionの__proto__が指す先はFunction.prototype

・全てのオブジェクトは__proto__プロパティを持つので、ObjectとFunctionも__proto__プロパティを持つはず。では、その参照先はどうなっているだろうか?
・ObjectとFunctionはいずれも__proto__プロパティがFunction.prototypeを参照している
・これはつまり、ObjectはFunctionから生成され、FunctionはFunction自身から生成されているといえる
・なので、ObjectもFunctionも、Cと同じく「Functionオブジェクト」であるといえる
f:id:maeharin:20130214141743j:image
検証:

Object.__proto__ === Function.prototype //true
Function.__proto__ === Function.prototype //true
C.__proto__ === Function.prototype //true

 

C.prototypeは空のオブジェクト

・ここで自分が生成した「C」に話を戻す。Cのprototypeはどのようなオブジェクトだろうか?
・Functionから生成したFucntionオブジェクトのprototypeは、デフォルトではnew Object()により生成されたオブジェクト(つまり空のオブジェクト)となる。なので、その空オブジェクトの__proto__プロパティはObject.prototypeを指す
f:id:maeharin:20130214141744j:image
検証:

C.prototype.__proto__ === Object.prototype //true

 

Functionオブジェクト.prototypeは、constructorプロパティを持つ

・少し脱線するが、全てのFunctionオブジェクト.prototypeはconstructorというプロパティを持つ
・なので、全てのFunctionオブジェクトはそのprototypeプロパティが参照する先のオブジェクトと相互リンクを張っている構図となる(もちろん、これは後で付け替えられるので常にこの関係が成立するわけではない)
f:id:maeharin:20130214141745j:image
検証:

Object.prototype.constructor === Object //true
Function.prototype.constructor === Function //true
C.prototype.constructor === C //true



prototypeの設定と新しいオブジェクトの生成

ここで冒頭のコードに戻って、以下の部分がどういう意味なのかを見る。

C.prototype.x ='xxx';

var c1 = new C('hoge');
var c2 = new C('fuge');

 

Functionオブジェクト.prototypeに新しいプロパティを追加する

・Functionオブジェクト.prototypeはオブジェクトなので、プロパティを追加することができる
・なのでC.prototype.x ='xxx'; で新しいプロパティxを追加することができる
f:id:maeharin:20130214141746j:image

Functionオブジェクトをnew演算子付きで呼び出すと、コンストラクタとなりオブジェクトを生成する

・Functionオブジェクトをnew付きで呼び出すと、コンストラクタとなって新しいオブジェクトを生成するようになる(newをつけないと通常の関数呼び出しになる)
・newによって生成されたオブジェクトは、__proto__をそれを生成したFunctionオブジェクトのprototypeに設定する
・なので、var c1 = new C('hoge');とすると、新しいオブジェクトが作成され、そのオブジェクトの__proto__がC.prototypeを参照するようになる
f:id:maeharin:20130214141747j:image

・さらに新しいオブジェクトをnew演算子で作成しても、同じように__proto__の参照先がC.prototypeを参照する
・なのでvar c2 = new C('fuge');とすると、c2の__proto__がC.prototypeを参照するようになる
・結果として、ここではc1とc2が両方とも自身に存在しないxを参照できるようになる
f:id:maeharin:20130214141748j:image
検証:

c1.x //xxx
c2.x //xxx

 

後からプロトタイプオブジェクトにプロパティを追加する

・では、ここでさらにC.prototypeに新しいプロパティを追加したらどうなるだろうか?冒頭のコードにおける以下の部分である。

C.prototype.y = 'yyy';

・c1もc2もC.prototypeを参照しているので、C.prototypeに追加したプロパティは既に作成したc1とc2に反映される
f:id:maeharin:20130214141749j:image
検証:

c1.y //yyy
c2.y //yyy

 

プロトタイプオブジェクトを全く新しいオブジェクトに取り替える

・では最後に、C.prototypeの参照先オブジェクトを全く新しいものに取り替えてしまったら、どうなるだろうか?
・冒頭のコードにおける最後の部分である。

C.prototype = {z: 'zzz'};
var c3 = new C('piyo');

 

・さきほどはC.prototypeに新しいプロパティを追加していたが、そうではなく全く新しいものに取り替える
・さらにその後var c3 = new C('piyo');で新しいオブジェクトを生成する
・すると、先に生成していたc1とc2の__proto__はそのまま残り、新しく生成したc3の__proto__だけが新しいC.prototypeを参照するようになる
・ここで注意しなければいけないのが、__proto__のconstructorプロパティである
・取り替える前は__proto__のconstructorプロパティがCを参照していたが、新しく生成したオブジェクトはただのオブジェクトであるため、c3.constructorはObjectを参照している。constructorが必ずしも自動的に張りなおされるわけではないので注意が必要である
f:id:maeharin:20130215122638j:image
検証:

c1.x //xxx
c2.x //xxx
c3.x //undefined

c1.z //undefined
c2.z //undefined
c3.z //zzz

c1.constructor === C //true
c2.constructor === C //true
c3.constructor === C //false

c1.constructor === Object //false
c2.constructor === Object //false
c3.constructor === Object //true



終わり

長くなったが、これで終わり!図にしたら各オブジェクトとプロパティの違いと参照関係がハッキリして、自分としてはとってもスッキリした!

 

原文地址:http://d.hatena.ne.jp/maeharin/20130215/javascript_prototype_chain

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics