js中基于prototype实现继承的基本代码如下所示:
function(SubClass, SuperClass){ function F(){} // F.prototype = SuperClass.prototype; // 实现继承的关键,构造 prototype chain SubClass.prototype = new F(); // 1 // 重置子类prototype对象的constructor属性为子类本身。 SubClass.prototype.constructor = SubClass; // 设置子类的superclass属性值为父类的prototype。 SubClass.superclass = SuperClass.prototype; // 2 // 使得在子类中可以通过baseconstructor来访问父类的构造函数。 SubClass.baseconstructor = SuperClass; }
上述代码中有两个值得关注的地方:
(1)语句1处使用了new F()的方式,这里为什么需要专门new一个F对象出来呢?首先我们来看下面的这段代码的结果:
function SP(){this.cls = "super class";} SP.prototype.print(alert(this.cls);); function SB(){} SB.prototype = SP.prototype SB.prototype.print(alert("changed by subclass")); new SP().print(); // output: changed by subclass
从结果可知SB prototype对象的改变同时影响到了SP,也即此时SB和SP是共享同一个prototype对象。这是不符合继承的本意的。这里如果将
SB.prototype = SP.prototype
更改为:
SB.prototype = new SP()
则可以避免此问题。这也是代码1处使用了new F() 而不是F.prototype的原因。
这里还有一个疑问就是F在这里具体作用是什么?为什么需要添加F,而不是直接使用new SuperClass() ?
先看下面这段代码:
function SP(){this.cls = "super class";} SP.prototype.print(alert(this.cls);); function SB(){} SB.prototype = new SP(); new SB().print(); // super class function F(){} F.prototype = SP.prototype; SB.prototype = new F(); new SB().print(); // undefined
代码中两次执行print函数打印出的结果分别是“super class” 和 undefined。也即第二次执行此函数时SB对象中不存在cls属性,这正是添加一个空的F函数的作用。它可以避免在子类SB中获得得到在父类SP函数体中定义的属性(this.xxx)。
(2)语句2处SubClass.superclass为SuperClass.prototype,而不是SuperClass本身。关于这一点的说明,引用 http://bbs.51js.com/viewthread.php?tid=72688&page=1&extra=#pid556697 上某位网友的观点:
subclass.superclass.call(this) 的方式没有subclass.superclass.constructor.call(this)的方式直观
而且构造函数constructor我感觉也是显示调用比较好,风格比较一致,而且少点潜规则也更容易看懂吧,没必要学java的那种方式,而且如果superclass没有构造函数的话 按照ext的方式会逐级向更上层的父类调用,直到最顶端。
Ext中的继承也是基于prototype chain的方式来实现的,在 http://www.iteye.com/topic/195409 有对其继承方法实现的详细解析。但是,其中有一处代码一直未弄懂其作用:
if(spp.constructor == Object.prototype.constructor){ spp.constructor=sp; }
这一代码中,if条件成立时有两种可能:(1)sp本身就是Object;(2)函数对象在创建时,其prototype对象的constructor属性值总是指向函数对象本身。因此另一种可能就是sp显示改变了其prototype对象,而新对象的constructor属性值指向的是Object。第1种情况处理没有任何意义,因为此时spp.constructor一定等于sp;那么只可能是第二种情况。一个令人费解的问题是"为什么要到有子类继承时才重置父类protoype对象constructor的值,而不是在显示改变其prototype对象时就设置"。为什么这里需要重置constructor的值呢?结合前后代码来看:
spp = sp.prototype; sb.superclass=spp; if(spp.constructor == Object.prototype.constructor){ spp.constructor=sp; }
同上文中关于语句2处设置子类superclass一样,此处sb的superclass属性设置为了spp,其父类sp的prototype对象。此时,若想访问到父类本身,就只能通过spp的constructor属性。因此,才需要对constructor属性进行判断,将其重置为父类sp。也即,加入此语句的原因是为了能够保证在子类中正确调用到父类的构造函数 ,而不是Object。