JS中各种继承方式对比

来源:互联网 发布:四大台柱 知乎 编辑:程序博客网 时间:2024/04/28 13:58

1.原型链

   不支持多重继承;不能很好的支持多参数或者动态参数的父类;不够灵活,用户需要在原型声明阶段实例化父类对象,并且把它作为当前类型的原型,会限制父类实例化的灵活性;包含引用类型的原型问题(引用类型值的原型属性会被所有的实例共享)

注意点1:给原型添加方法的代码一定要在替换原型之后

function SuperType(){  this.property=true;}SuperType.prototype.getSuperValue=function(){  return this.property;}function SubType(){  this.subProperty=false;}SubType.prototype=new SuperType();//继承SubType.prototype.getSubValue=function(){  return this.subProperty;}//设置SubType的prototype值必须在上一句代码下面//否则即使设置了也是没有效果的!
注意点2:不能使用对象字面量创建原型方法

function SuperType(){  this.property=true;}SuperType.prototype.getSuperValue=function(){  return this.property;}function SubType(){  this.subProperty=false;}SubType.prototype=new SuperType();//继承SubType.prototype={ getSubValue:function() {   return this.subproperty; }, someOtherMethod:function() {   return false; } //使用字面量添加新方法,会导致上一行代码无效,相当于又重写了prototype!}

2.构造函数继承(解决了原型中包含引用类型值带来的问题)

function SuperType(name){   this.name=name;this.colors=["red","green"]}function SubType(){SuperType.call(this,"xxx");//借用了父类的构造函数,所以每一个子类都具有一个colors副本.同时子类可以向父类中传递参数!SuperType.call(this,“xxx”);}

缺点:方法都在构造函数中定义,函数复用无从谈起;超类型中的原型中定义的方法对子类型来说是不可见的,结果所有的类型只能使用构造函数模式!

function SuperType(name){        this.name=name;this.colors=["red","green"]}SuperType.prototype.getAge=function(){  alert("我可以得到年龄!");}function SubType(){SuperType.call(this,"xx");}var sub=new SubType();alert(typeof sub.getAge);//这里返回undefined,因为这种通过借用构造函数的方式只能继承实例属性,无法继承原型属性,所以才有了用call继承实例属性,用prototype继承原型属性的组合继承方式

3.组合继承

用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承!instanceof和isPrototypeOf能够识别基于组合继承的对象

function SuperType(name){  this.name=name;  this.color=["red","blue","green"];}SuperType.prototype.sayName=function(){  console.log(this.name);}function SubType(name,age){ SuperType.call(this,name); //通过借用构造函数来对实例属性的继承 this.age=age;}SubType.prototype=new SuperType();SubType.prototype.constructor=SubType;//如果没有修改constructor那么这时候就是SuperType!SubType.prototype.sayAge=function(){  console.log(this.age);}var instance=new SubType("qinliang",27);var instance1=new SubType('fkl',21);console.log(instance.hasOwnProperty('name'));console.log(instance.hasOwnProperty('age'));console.log(instance.hasOwnProperty('color'));//都打印true,也就是说name,age,color都是通过借用构造函数的方式继承!//这首后不管有没有上面几句代码对SubType.prototype操作,结果都相同!console.log(instance.hasOwnProperty('sayName'));//通过原型链实现对原型属性和方法的继承,sayName依然在SuperType.prototype中//所以打印falseconsole.log(SubType.prototype.hasOwnProperty('sayName'));//打印false,sayName依然在SuperType的prototype中console.log(SuperType.prototype.hasOwnProperty('sayName'));//打印trueconsole.log(instance.constructor);//这时候构造函数类型是SubType了!console.log(instance1.sayName===instance.sayName);//打印true

我们需要知道:必须设置SubType.prototype.constructor=SubType,否则创建出来的对象的constructor就是SuperType类型了;如果仅仅需要继承实例属性,那么我们可以用借用构造函数的方法,如下:

function SuperType(name){  this.name=name;  this.color=["red","blue","green"];}function SubType(name,age){ SuperType.call(this,name); //通过借用构造函数来对实例属性的继承 this.age=age;}
但是借用构造函数的方法有两个缺点(见上面构造函数继承):首先,父元素的原型中的方法对子类不可见;然后,方法都在构造函数中定义,因此函数复用无从谈起

function SuperType(name){  this.name=name;  this.color=["red","blue","green"];  //其实是new Function()构造  this.sayAge=function()  {    console.log(this.age);  }}function SubType(name,age){ SuperType.call(this,name); //通过借用构造函数来对实例属性的继承 this.age=age;}SuperType.prototype.sayName=function(){  console.log("xxx");}var instance=new SubType('ql',21);var instance1=new SubType('fkl',23);console.log(instance.hasOwnProperty('sayName'));//这时候父类原型中定义的方法对子类是不可见的!console.log(instance.sayAge===instance1.sayAge);//打印false,函数复用无从谈起

组合式继承的不足:在任何情况下,都会调用两次超类的方法,第一次在SubType.prototype=new SuperType()得到两个原型属性,第二次调用在SuperType.call(this,name)得到同名的两个实例属性,同时实例属性会屏蔽原型属性,因此才有了寄生组合式继承!

     var instance=new SubType("qinliang",27);var instance1=new SubType('fkl',21);      console.log(instance.hasOwnProperty('name'));console.log(instance.hasOwnProperty('color'));console.log(SubType.prototype.hasOwnProperty('name'));console.log(SubType.prototype.hasOwnProperty('color'));//都打印true,这说明在实例中和原型中都有了相同的属性//而且原型属性会被实例属性屏蔽掉,所以是无用的!

4.原型式继承

function object(o){    function F(){}        F.prototype=o;//让传入的对象作为原型,很显然,如果该传入的对象具有引用类型,如数组,那么所有的返回的new F()都会继承这个数组,那么所有的对象会共享这个数组,//其它对象对他的操作都会导致该数组元素的变化      return new F();}

上面这种原型式继承的方式可以参考该图,ECMAScript5添加了Object.create方法规范了该方法,第一个参数是用作原型的对象,第二个参数是为新对象定义额外属性的对象。如果第二个参数和第一个参数也就是原型对象属性相同,原型中的同名属性会被屏蔽

5.寄生式继承

   有点像java中的装饰设计模式,增强对象,但是其思路是原型式继承

function createAnother(original){    var clone=object(original);//以original为原型对象的实例对象,调用上面的object函数,得到original的一个子类对象! clone.sayHi=function(){alert("Hi")}//为这个返回的对象进行包装装饰,增强功能。但是这种方式和构造函数一样,为对象添加函数,会由于不能做到函数复用而降低效率!       return clone;}

寄生式继承的原理还是来自于原型式继承,只是寄生式继承在返回对象之后还给对象添加了特有的方法,然而在原型式继承中只是简单的返回就可以了,没有实现增强

function object(o){  function F(){}  F.prototype=o;  return new F();}//寄生式继承函数function parasite(original){  var clone=object(original);  //增强对象  clone.sayHi=function()  {    console.log('Hello');  }  return clone;}var person={  name:"liangklfang",  friends:['ql','fkl'],}var augment=parasite(person);//寄生式继承的原理是原型式继承!augment.sayHi();//打印hello,完成了对象的增强   console.log(augment.constructor);   //augment的原型是person对象,但是person对象没有constructor   //所以往上搜索到person的原型,所以是function Object(){[native code]};   //如果给person对象添加constructor:"Person",那么对constructor的搜索到person为止!

寄生式继承来为对象添加函数,会由于不能得到函数复用而降低效率,这一点和构造函数模式类似!

6.寄生组合式继承

  function inheritProperty(subType,superType){   var prototype=object(supertType.prototype);//得到超类型原型的一个副本,认真看懂object方法的new F()把!      prototype.constructor=subType;//设置constructor属性     subType.prototype=prototype;//设置prototype属性  //通过以上步骤完成了从subType到SuperType.prototype的原型链的构造}

通过借用构造函数来继承属性,通过原型链的方式来继承方法;背后的思路是不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非是超类型的一个副本(object方法获取)而已,本质上就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型:

function object(o){  function F(){}  F.prototype=o;  return new F();}//寄生组合式继承的本质!   function inheritProperty(subType,superType)   {     var prototype=object(superType.prototype); prototype.constructor=subType; //如果不修正,那么构建的SubType实例constructor就是SuperType而不是SubType subType.prototype=prototype;   }function SuperType(name){  this.name=name;  this.color=['red','blue','green'];}SuperType.prototype.sayName=function(){  console.log(this.name);}//继承父元素的name和color属性,通过构造函数完成!function SubType(name,age){  SuperType.call(this,name);  this.age=age;}inheritProperty(SubType,SuperType);SubType.prototype.sayAge=function(){  console.log(this.age);}  var instance=new SubType('liangklfang',12);  var instance1=new SubType('liangklfang',22);  console.log(instance);  console.log(instance instanceof SubType);  //打印true,说明是SubType类型   console.log(instance instanceof SubType); //打印true,表明是SuperType类型 console.log(instance.constructor); //在函数inheritProperty中修正了constructor为SubType //如果没有修正就是SuperType! console.log(instance.hasOwnProperty('name')); console.log(instance.hasOwnProperty('color'));//所有name,color都在实例中间,而不再原型中间! console.log(SubType.prototype.hasOwnProperty('name')); console.log(SubType.prototype.hasOwnProperty('color'));//打印false表示这时候在SubType的原型中没有name/color了console.log(instance1.sayName===instance.sayName);//方法通过原型链共享,而属性通过借用构造函数继承获取副本
通过该图你能够清楚的认识到,我们是如何获取到SuperType的副本的,从而实现了把所有的属性保存到实例中而不是在原型中也保存一个副本,这一点是组合式继承没有办法完成的!

0 0
原创粉丝点击