继承
本文于479天之前发表,文中内容可能已经过时。如有疑问,欢迎在github中给我留言(用户名:zhangyan123)。
继承实现原理(从上到下由浅入深)
原型链继承模式
function Shape(){this.name='shape';};
Shape.prototype.toString=function(){
return this.name;};
function Triangle(side,height){
this.name='triangle'; this.side=side; this.height=height;}
Triangle.prototype.getArea=function(){return this.side*this.height/2;};
Triangle.prototype=new Shape();
Triangle.prototype.constructor=Triangle;这种继承模式除了共享了“父类”(模拟说法,后面部分均直白表示为父类和子类)原型中的属性和方法之外,在原型上父类个性拥有的name属性,造成不必要资源浪费。
</div
临时构造器继承模式
根据之前的分析知道Triangle.prototype=new Shape();语句造成了对多余父类属性的拷贝,为避免不共享的成员不被拷贝,我们可以启用临时“空”(只扮演传递父类原型指针的角色)构造器,并指定其原型属性指向父类原型。以此解决上述问题。function Shape(){ this.name='shape'; }; Shape.prototype.toString=function(){ return this.name; }; function Triangle(side,height){ this.name='triangle'; this.side=side; this.height=height; } var F=function (){}; F.prototype=Shape.prototype; Triangle.prototype=new F(); Triangle.prototype.constructor=Triangle; Triangle.prototype.getArea=function(){ return this.side*this.height/2; };进一步优化function Shape(){ this.name='shape'; } Shape.prototype.toString=function(){ return this.name; }; function Triangle(side,height){ this.name='triangle'; this.side=side; this.height=height; } var F=function (){ this.getArea=function(){ return this.side*this.height/2; }; }; F.prototype=Shape.prototype; Triangle.prototype=new F(); Triangle.prototype.constructor=Triangle;现在我们已经具备了一种比较良好的继承模式,既可以继承到父类的共享内容,又可以在子类中添加个性的成员而不对其父类造成影响,下面我们在核心功能的基础上增强继承的能力,增加子类访问父类成员的通道(模拟java super,super是js保留字故采用德语中的同义词uber替代)。function Shape(){ this.name='shape'; } Shape.prototype.toString=function(){ return this.name; }; function Triangle(side,height){ this.name='triangle'; this.side=side; this.height=height; } var F=function (){ this.getArea=function(){ return this.side*this.height/2; }; this.uber=Shape.prototype; }; F.prototype=Shape.prototype; F.uber=Shape.prototype; Triangle.prototype=new F(); Triangle.prototype.constructor=Triangle;工具集:
function extend(Child,Parent){ var F=function(){}; F.prototype=Parent.prototype; Child.prototype=new F(); Child.prototype.constructor=Child; Child.uber=Parent.prototype; }前面介绍了原型链继承模式,在模拟传统继承方式时还可以简单粗暴的将父类的原型中成员直接拷贝到子类原型中。
工具集:function extend2(Child,Parent){ var c=Child.prototype,p=Parent.prototype; for(var i in p){ c[i]=p[i] } c.uber=p; }值得注意的是prototype中的constructor属性是不可枚举的,在for-in循环中不会被拷贝,子类保有自己正确的constructor指向。 以上拷贝很明显是浅拷贝,当父类中有对象成员,甚至对象成员中还有对象子成员时,以上拷贝会造成引用传递,子类在修改属性时引发父类属性同时变化,下面介绍深拷贝工具集。 工具集:function deepcopy(p,c){ c = c||{}; for(var i in p){ if(p.hasOwnProperty(i)){ if(typeOf p[i] ==== 'object'){ c[i] =Array.isArray(p[i])?[]:{}; deepCopy(p[i],c[i]); }else{ c[i]=p[i]; } } } return c; }ES5中实现了isArray,为了向下兼容补充工具集如下(全局): if(Array.isArray !=='function'){ Array.isArray = function(args){ return Object.prototype.toString.call(args) === '[object Array]'; }; }前面介绍了原型链继承模型和简单的成员拷贝模式,我们在继承当中不仅要复用父类的方法还会有增加新方法的需求,因此结合两种方式模式增强我们的继承工具集如下:
工具集(基于构造器模式):function extendX(P,stuff){ var n; function F(){}; F.prototype=P.prototype; n=new F(); n.uber=P.prototype; for(var i in stuff){ n[i]=stuff[i] } return n; }
工具集(基于对象模式):
function extendX(o,stuff){
var n;
function F(){};
F.prototype=o;
n=new F();
n.uber=o;
for(var i in stuff){
n[i]=stuff[i]
}
return n;
}
- 借用构造器+原型复制 模式实现继承
借用构造器使得我们可以在子类构造器中调用福构造器,但是此处apply调用是方法调用,不会继承父类的原型属性,为了补充这块空缺,可以使用extend2或者deepCopy函数进行原型拷贝,让子类继承到父类的成员与原型成员。
借用构造器形式举例:function Triangle(){ Shape.apply(this,arguments); }