JavaScriptのプロトタイプチェーンについて理解しようとしたのだけど、prototypeとか__proto__とかごちゃごちゃになって、色んなブログを読んでもなかなか理解しきれなくて悶々としていたのだが、図を書いたらパッと理解できた!以下、情報ソースはなるべくECMAScript仕様書(3rd)を元にするようにして書きました
なぜ分かりづらいのか?
そもそも、なぜJavaScriptのプロトタイプチェーンは自分にとってこうも分かりづらかったのだろうか?自分なりに分析してみると、まず、「似ているが違う用語が沢山ある」という点がある。ざっとあげただけでも、「prototypeと__proto__」「__proto__と[[Prototype]]」「FunctionとFunctionオブジェクト」などがある。そして次に、「入り組んだ構造が動的に変化する」という点がある。上記のように似たような用語が沢山出てくる上に、それらの構造が入り組んでおり、しかもそれらが動的に変化していくのだ。これは自分の脳みそではきつい。さらに、__proto__プロパティは処理系によっては実装されていない(ChromeやFirefoxならおk)など「環境依存」の部分もある。自分が中々理解できなかったのはこういった要素に原因があるのではと思った。
図にすれば理解しやすくなる!
なので、上記を解決するために「図」を用いることにした。それにより「用語が明確に区別され」「構造を状態ごとに追う事ができる」と思ったからだ。ということで、JavaScriptのオブジェクト構造がどのようになるかを図で可視化していく。図は以下のように「丸はオブジェクト。矢印はプロパティ」というシンプルな図を用いることにする。
サンプルコード
以下のサンプルコードを実行した時のオブジェクト構造を図にする
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にオブジェクトをセットしていることになる
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オブジェクト」である
全ての「Functionオブジェクト」はprototypeプロパティを持つ
・では、Functionオブジェクトとはどのようなオブジェクトだろうか?
・まず、全てのFunctionオブジェクトはprototypeというプロパティを持つ
・そしてprototypeプロパティの先には通常何らかのオブジェクトがセットされている
・実はFunctionも自身から生成されたFunctionオブジェクトである
・なので、FunctionもCもprototypeプロパティをもつ
全ての「オブジェクト」は__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__という呼び名で統一することにする
Functionオブジェクト.__proto__は、Function.prototype
・__proto__プロパティの参照先を詳しくみていく
・まず、「Functionオブジェクト(ここではC)」の__proto__プロパティは、Function.prototypeを参照する
__proto__があると何が嬉しいのか?
・自分自身に存在しないプロパティ/メソッドが呼び出された時、__proto__を辿った先にあるオブジェクトにそのプロパティ/メソッドが無いか探し、あればそれを使う
・例えば、Function.prototypeにはtoStringメソッドが存在する。なので、以下のようにするとtoStringが呼び出せる
・C自身はtoStringメソッドを持っていないが、__proto__を辿った先にあるFunction.prototypeがtoStringメソッドを持っているので、それが呼び出される。
C.toString(); //"function (name) { // this.name = name; //}"
検証:
(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();
検証:
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オブジェクト」であるといえる
検証:
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を指す
検証:
C.prototype.__proto__ === Object.prototype //true
Functionオブジェクト.prototypeは、constructorプロパティを持つ
・少し脱線するが、全てのFunctionオブジェクト.prototypeはconstructorというプロパティを持つ
・なので、全てのFunctionオブジェクトはそのprototypeプロパティが参照する先のオブジェクトと相互リンクを張っている構図となる(もちろん、これは後で付け替えられるので常にこの関係が成立するわけではない)
検証:
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を追加することができる
Functionオブジェクトをnew演算子付きで呼び出すと、コンストラクタとなりオブジェクトを生成する
・Functionオブジェクトをnew付きで呼び出すと、コンストラクタとなって新しいオブジェクトを生成するようになる(newをつけないと通常の関数呼び出しになる)
・newによって生成されたオブジェクトは、__proto__をそれを生成したFunctionオブジェクトのprototypeに設定する
・なので、var c1 = new C('hoge');とすると、新しいオブジェクトが作成され、そのオブジェクトの__proto__がC.prototypeを参照するようになる
・さらに新しいオブジェクトをnew演算子で作成しても、同じように__proto__の参照先がC.prototypeを参照する
・なのでvar c2 = new C('fuge');とすると、c2の__proto__がC.prototypeを参照するようになる
・結果として、ここではc1とc2が両方とも自身に存在しないxを参照できるようになる
検証:
c1.x //xxx c2.x //xxx
後からプロトタイプオブジェクトにプロパティを追加する
・では、ここでさらにC.prototypeに新しいプロパティを追加したらどうなるだろうか?冒頭のコードにおける以下の部分である。
C.prototype.y = 'yyy';
・c1もc2もC.prototypeを参照しているので、C.prototypeに追加したプロパティは既に作成したc1とc2に反映される
検証:
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が必ずしも自動的に張りなおされるわけではないので注意が必要である
検証:
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
相关推荐
JavaSciptDOM基本操作,JavaScipt函数基础,JavaScipt流程语句,JavaScript变量,JavaScript数据类型,JavaScript数组,JavaScript正则表达式,JavaScript字符串函数,Window对象等图解。JS高手进阶的工具图谱
下面小编就为大家带来一篇[js高手之路]图解javascript的原型(prototype)对象,原型链实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
个人总结的原型链全套图解,可以辅助你对原型链的理解,拿走拿走
主要介绍了图解JS原型和原型链实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
javascript 知识点图解 JavaScript 数据类型 、JavaScript 变量、Javascript 运算符、JavaScript 数组、JavaScript 函数基础、DOM 基本操作、Window 对象、JavaScript 字符串函数、正则表达式
图解 javascript 结构 让你对javascript 有全面的了解
javascript总结,以图片的形式归纳javascript常用的函数已经注意事项
于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一、把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 function Person(){ this.userName =...
夯实基础上篇-图解 JavaScript 执行机制.doc
先来一段简单的javascript代码: ...1.javascript引擎会在页面加载脚本被执行时为每个函数创建一个作用域(执行上下文)及作用域链。 2.javascript引擎在产生这些作用域后,会创建一个堆栈。 3.将onload对应的
Object是一个属性的集合,并且都拥有一个单独的原型对象[prototype object]. 这个原型对象[prototype object]可以是一个object或者null值。 让我们来举一个基本Object的例子,首先我们要清楚,一个Object的...
js流程,正则,字符串,操作,window对象,变量,运算,函数,数据类型
而关于原型,则是prototype、proto和constructor的三角关系。本文先用一张图开宗明义,然后详细解释原型的三角关系 图示 概念 上图中的复杂关系,实际上来源就两行代码 function Foo(){};var f1 = new Foo; ...
img镜像转vmdk详细过程图解 镜像转换过程,vmware格式
夯实基础中篇-图解作用域链和闭包.doc
建筑消防设施操作图解-超链版参考.pdf
1.原型链为o->MyClass.prototype。 2.函数如果没有明确返回值默认返回this。 由上图可得:call和apply功能相同,不同之处为apply传调用参数时应为数组。 由上图可得:bind传的对象即作为this。
惠普HP1215|HP1210| HP1518|HP1515更换转印带拆机图解教程