A) 关于prototype chain有几点说明:
a. 这个链的终点是Object的prototype对象:Object.prototype
b. 所有的对象在默认的情况下都有一个原型(__proto__).
c. 原型本身也是对象,所以每个原型自身又有一个原型(__proto__),除了Object.ptototype.
关于c这个观点可以用一下面的小程序来证明:
function A(){} console.log(A.prototype.__proto__);//Object {} console.log(Object.prototype.__proto__);//null
就这上面两个输出,下面做详细的分析,并说明这个proto链是怎么形成的。
1) 先定义一个简单的函数并创建该函数的对象 var a = new A(); 注意,这里a,没有自己的属性,它的所有的属性都从原型对象上继承而来)现在看看内存中这个对象的具体情况
图 1.1
观察图1.1,可以发现a对象的__proto__属性,这个论证了观点b的正确性。观察prototype对象有一个__proto__属性(绿色方框所示)。这个就说明了观点b的正确性。
但是,prototype的__proto__所代表的对象是谁呢?查看@37891发现,这个对象是一个Object对象,其实说白了就是Object.prototype所指向的对象。下面给出该对象的内存情况:
图 1.2
观察这个对象的constructor,其指向Object的构造器。对比图1.1可以发现,这个对象是没有__proto__属性的,这个对象正是a对象所在的proto链条的终点:Object.pototype.
结合上面的说明,可以得到如下图示:
注意:沿着红色箭头组成了proto链!!!所以console.log(A.prototype__proto__)为Object{},而Object.prototype.__proto__为null.
B) 通过这个prototype chain可以实现属性的继承,例程如下:
function A(x){ this.x = x ; } function B(y) { this.y = y; } B.prototype = new A(1); function C(z) { this.z = z; } C.prototype = new B(2); var c = new C(3); var sum = c.x+c.y + c.z;//sum == 6可以通过chrome浏览器的Take Heap Snapshot来查看对象c的prototype chain。如下图
图 1.4
先逐步分一下各个对象的内存分析图:查看一下@14285对象(A的对象)的prototype chain:(留意一下@后面的数字)。
图 1.5
用图形表现其prototype chain如下:
图1.6
通过上图分析,有如下关系:
a.__proto__ === A.prototype, A.prototype.__proto__===Object.prototype
再看看@14295所代表的对象(B的对象)的prototype chain:
图1.7
用图形表现其prototype chain如下:
图1.8
b.__proto__ === B.prototype B.prototype.__proto__ === A.prototype A.prototype.__proto__ === Object.prototype
说明:图1.8的B.prototype所代表的对象,其实是图1.6中的对象a,因为B.prototype的@后面的数字和a对象@后面的数字是一样的。所以在这里特地把它们的颜色标识为绿色,以示为同一个对象。其余颜色相同的@+数字,代表着同样的意思。
所以图1.8和图1.7合并起来可以另外表示为:
图 1.9
到这里,B的prototype chain已经和A的prototype chain连接起来了。链接起来的关键代码就是B.prototype = new A(1);
同理,再看看对象c的prototype chain:
图 2.0
用图形表示为:
图 2.2
C.__proto__=== C.prototype
C.prototype. __proto__ == a
注意蓝色@14295,可以得出c. __proto__== b,在代码中体现为C.prototype = new B(2);
所以图2.1还可以表示为:
至此,c的prototype chain 完全构成。最后就可以很清晰的得出程序最终的结果6是如何得到的了
当c访问x即c.x时,c对象里有x属性,直接获取它的值1,但是c.y这个属性在c对象里没有,所以会沿着prototype chain查询,在b对象中也就是C.prototype中找到y,取值为2,同理c.z在a对象中找到了z属性。因此sum == 6
同时也可以发现,javascript对象属性搜索的过程是由近到远的顺序,如果c对象中有了y属性,那么c.prototype的属性y是不会访问到的。
Var c = new (3);
c.y = 4;//
var sum = c.x +c.y ;//==5而不是==3
注意代码c.y = 4;这是个赋值操作,而不是取值操作;对于赋值操作,javascript总是在原始对象(在这里是c对象)创建属性或者对已有的属性赋值,而不会去修改原型链。在javascript中只有查询属性(取值操作)时才会体现到继承的存在,而设置属性则和继承无关。