ECMAScript 3rd阅读笔记之一——js prototype链解惑
众所周知,js不是class-based的,而是object-based。在ecma-262中,开篇对js的继承机制做了简单的描述(见 ecma-262 4.2.1)。其中提到,js中的对象搜索属性的一个链。首先,对象搜索自己定义的属性(即用obj.property的形式直接赋值的属性),如果不存在,则在对象隐含的prototype引用去找,而这个隐含的prototype引用即是其构造函数的prototype属性。这里要注意的是,构造函数的prototype属性仍旧是一个对象。所以,仍旧按照上面的描述规则来进行属性搜索,直到最终找到这个属性或者不存在隐含的prototype引用(即再也不能通过链属性了)为止。这就是ecma-262中的prototype chain。
为了形象点,我们可以把prototype chain用最形象的图来表示,它就是一条链子:
c(=new b())
|
|—-p=2
|
b.prototype
|
|——–p=1
|
…………
相应代码如下:
- var b=function() {
- }
- b.prototype.p=1;
- var c=new b;
- c.p=2;
b是一个构造器,c通过new初始化一个b的实例。然后,c在“修改”p的值为2。需要注意的是,我在修改这个词上加了引号,这是有原因的。
先来看看,如果运行alert(c.p),好无疑问,值是2。那么,假如去掉代码的第6行即c.p=2,此时,在c对象的属性中找不到p,就去找c隐含的prototype引用,即b.prototype。于是找到了b.prototype.p=1,所以输出就是1。又假如去掉代码的第3行即b.prototype.p=1,那么找不到p这个属性,又会去找b.prototype隐含的prototype引用(例如有b.prototype=new a(),而a.prototype.p=0),以此类推。最终等到找到p或者无法再沿隐含的prototype链走下去为止。
所以,事实,当c初始化为b的实例时,访问c.p,并不是真正访问c对象中的p属性,而是c在prototype链上的p属性。于是,假如在初始化后,用语句c.p=3来“修改”p的值,其实不是“修改”,而是覆盖。即用c对象自己的p属性覆盖了在prototype链上的p属性。而之后是仍旧可以找回prototype链上的p属性的。关于此方法请参见我的另一篇文章《找回被覆盖掉的原型属性》
诚然,prototype链确实很晦涩,对初学者也难以理解。对于final user,大可以按照习惯的“继承”思路来看待他们。而对于使用js来进行二次开发的developer,却必须搞清楚令人即爱又恨的prototype chain。
在此后的文章中,将进一步讨论prototype链的问题,同时将再引入另一个奇怪的属性__proto__。它是对象的隐含属性,它的存在,就更加让人匪夷所思了。
[此贴子已经被作者于2007-11-16 18:19:28编辑过]