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

【转】图解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

分享到:
评论

相关推荐

    JavaScript原型链简单图解

    JavaSciptDOM基本操作,JavaScipt函数基础,JavaScipt流程语句,JavaScript变量,JavaScript数据类型,JavaScript数组,JavaScript正则表达式,JavaScript字符串函数,Window对象等图解。JS高手进阶的工具图谱

    原型链全套图解.rar

    在JavaScript中,原型链是理解对象继承和属性查找机制的关键概念。它是一种基于原型(prototype)的继承方式,使得一个对象能够从另一个对象那里继承属性和方法。在本压缩包"原型链全套图解.rar"中,包含了全面的...

    [js高手之路]图解javascript的原型(prototype)对象,原型链实例

    JavaScript中的原型(Prototype)是语言的核心特性之一,它在实现对象继承和共享方法时起着至关重要的作用。本文将深入探讨原型对象、原型链的概念,并通过实例解析它们的工作原理。 首先,每个函数都有一个名为`...

    图解JS原型和原型链实现原理

    JavaScript中的原型和原型链是其继承机制的核心,理解这两者对于深入学习JS至关重要。本文将通过详细的图解和实例代码,帮助你理解这两个概念及其实现原理。 首先,我们需要明白JavaScript的发展背景。JavaScript...

    javascript图解

    4. **原型与原型链**:JavaScript对象都有一个`__proto__`属性,指向创建它的函数的原型。通过原型链,对象可以访问到原型上的属性和方法,实现继承。 5. **对象**:JavaScript对象是键值对的集合,可以使用字面量...

    javaScript核心原理

    #### 原型链(Prototype Chain) **原型链**是一个由多个对象组成的链表,用于实现继承和共享属性。当查找一个对象的属性或方法时,JavaScript会先在该对象本身查找,如果没有找到,则会沿着该对象的原型链向上查找,...

    javascript知识点图解

    javascript 知识点图解 JavaScript 数据类型 、JavaScript 变量、Javascript 运算符、JavaScript 数组、JavaScript 函数基础、DOM 基本操作、Window 对象、JavaScript 字符串函数、正则表达式

    详解帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    当我们尝试访问一个对象的属性时,如果该对象自身没有这个属性,JavaScript会沿着`__proto__`链向上查找,直到找到该属性或到达原型链的顶端——`null`。这个查找过程形成了所谓的原型链。 其次,`prototype`属性是...

    图解javascript结构

    【图解JavaScript结构】 在深入理解JavaScript的结构之前,我们首先要明确JavaScript是一种广泛使用的脚本语言,主要用于网页和网络应用的开发。它以其灵活、动态的特性,以及与HTML和CSS的良好集成,使得网页交互...

    [js高手之路]从原型链开始图解继承到组合继承的产生详解

    首先,我们要了解JavaScript的原型链(Prototype Chain)。每个对象都有一个内部属性`__proto__`,它指向创建该对象的构造函数的原型。当试图访问对象上的某个属性或方法时,如果该对象本身没有这个属性,就会查找其...

    javascript完全图解

    "javascript完全图解"这一主题涵盖了JS的基础到进阶概念,旨在帮助开发者全面理解并熟练运用JavaScript。 1. **流程控制**:JavaScript提供了多种流程控制结构,包括顺序执行、条件判断(if...else, switch...case...

    图解javascript作用域链

    JavaScript的作用域链是理解JavaScript执行环境的关键概念,它决定了变量和函数的可访问性。在JavaScript中,每个函数都有自己的作用域,而这些作用域按照特定的顺序组织起来,形成了作用域链。这个链帮助解析器在...

    夯实基础上篇-图解 JavaScript 执行机制.doc

    夯实基础上篇-图解 JavaScript 执行机制 本文主要讲述了 JavaScript 执行机制的基础知识,包括变量提升、执行上下文、调用栈等概念。本文通过 9 个 demo 和 18 张图,详细地解释了 JavaScript 执行机制的过程。 ...

    图解prototype、proto和constructor的三角关系

    在JavaScript中,prototype、proto(或__proto__)和constructor是理解对象...这个关系图解帮助我们理解 JavaScript 中的原型继承和对象之间的关联。了解这些基本概念对于深入学习 JavaScript 的面向对象编程至关重要。

    构造函数 原型对象 实例、图解

    原型对象是 JavaScript 中的一个重要概念,它是对象的原型链的起点。每个对象都有一个原型对象,原型对象定义了对象的行为和属性。原型对象是通过构造函数创建的,在构造函数中,我们可以使用 `prototype` 属性来...

    帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    `__proto__`是一个对象到对象的引用,当试图访问对象的一个属性时,如果对象本身没有这个属性,JavaScript引擎会沿着原型链向上查找,直到找到该属性或者到达原型链的终点——`null`。这种通过`__proto__`连接形成的...

    prototype与__proto__区别详细介绍

    在JavaScript中,`prototype`和`__proto__`都是与对象继承和原型链紧密相关的概念,但它们在用途和性质上有所不同。以下是这两者的主要区别和详细解释: 1. **prototype(原型)** - `prototype`是函数特有的属性...

Global site tag (gtag.js) - Google Analytics