在正式讨论js继承之前,先说说自己的工作体会。不像后端,对于大多数商业应用场景,对前端的要求往往会导致代码难以上溯(指难以抽象,难以写一遍代码在好几个场景都适用)。说个我熟悉的应用场景:比如一个信用评级模块,一个页面用一个折线图显示用户最近的消费水平,另一个页面用一个折线图展示用户的信用评级水平。看似很相似的需求吧?拿到java里肯定抽象出一个“折线图接口“了吧,但是在js里就很难统一。首先,这两个数据来源不同,那么ajax请求的代码就天差地别。其次,UI随便给一个图加个阴影、渐变色之类的图形效果,两个图的实现就会完全不同。不仅如此,如果你为了展示自己的设计水平,强行把两块有区别的前端实现整合在一起,你将会面对常人不会面对到的维护噩梦。因此,对于大部分前端工程师而言,没有了解JavaScript继承的需求,这个语言的这个功能也确实没有一个很大众化的应用场景,当然如果你在编写某些底层框架,那你一定会很了解这些特性。以下内容难度中上,我稍微改写了一点《JavaScript高级程序设计》当中的继承,然后放了一段jQuery当中的继承模型优化版,以后有时间有精力再补充文字说明吧,不感兴趣的可以直接过了~
(1)组合继承(combination inheritance):
在子类构造函数中通过Super.call(this)来添加父类的属性,并通过让子类构造函数的prototype成为父类的实例来实现继承。缺点是会在子类构造函数中存储冗余的父类属性(因为他是父类的一个实例,而子类的实例又会覆盖一遍同样的属性),同时会调用两次父类的构造函数,并且也会导致继承链多了一层。
(function combineInherit(){
function SuperType(){
this.super="super";
}
SuperType.prototype.saySuper=function(){
}
function SubType(){
SuperType.call(this,arguments);
this.sub="sub";
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.saySub=function(){
}
var sub1=new SubType();
sub1.saySub();
})();
继承链:
(2)寄生组合式继承(parasitic combination inheritance):
与组合继承不同在于,寄生组合式继承另子类构造函数的prototype不成为父类的实例,而是直接通过父类构造函数的prototype的引用来形成子类构造函数的prototype,最精简的方式就是另二者直接等同,这样不仅减少了一次父类构造函数的调用,减少了存在子类prototype中的冗余属性,同样也减少了一层继承链(并且这种情况下用instanceof判断仍然同属子类和父类)。
(function parasiticCombineInherit(){
function inheritPrototype(subType,superType){
subType.prototype=superType.prototype;
subType.prototype.constructor=subType;
}
function SuperType(){
this.super="super";
}
SuperType.prototype.saySuper=function(){
}
function SubType(){
SuperType.call(this,arguments);
this.sub="sub";
}
inheritPrototype(SubType,SuperType);
SubType.prototype.saySub=function(){
}
var sub=new SubType();
console.log(sub instanceof SuperType);
})();
继承链:
(3)jQUery式经典继承
(function classicInherit(){
var initializing=false,
superPattern=/xyz/.test(function() {xyz;}) ? /\b_super\b/ : /.*/;
Object.subClass=function(properties){
var _super=this.prototype;
initializing=true;
var proto=new this();
initializing=false;
for(var name in properties){
proto[name]=typeof properties[name]=="function"&&
typeof _super[name]=="function"&&
superPattern.test(properties[name])?
(function(name,fn){
return function(){
var tmp=this._super;
this._super=_super[name];
var ret=fn.apply(this,arguments);
this._super=tmp;
return ret;
};
})(name,properties[name]):properties[name];
}
function Class(){
//init方法需要通过外部自定义传入
if(!initializing&&this.init){
this.init.apply(this,arguments);
}
}
Class.prototype=proto;
Class.constructor=Class;
Class.subClass=arguments.callee;
return Class;
}
})();