Javascript继承
2016-06-01 15:26
423 查看
一、原型链继承方式
1、原形链继承做法示例:
function SuperType(){ this.property=true; } Super.prototype.getSuperValue=function(){ return this.property; } function SubType(){ this.subProperty=false; } SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function(){ return this.subProperty; } var s=new SubType(); console.log(s. getSuperValue());
2、优势
原形链的优势来自于共享,如getSuperValue,无论new多少个SubType,都只共享同一个getSuperValue
3、缺点
只能实现单一继承。
例如SubType.prototype=new SuperType1();如果还想让SubType.prototype再继承SuperType2,那么SubType.prototype=new SuperType2 ();结果是SubType将不再继承SuperType1而只继承SuperType2
没有很好地解决属性共享问题
这是原形链最大的问题。例如:
function SuperType(){ this.names={“Leo”,”Linda”}; } SuperType.prototype.getName=function(){return this.name}; function SubType(){} SubType.prototype=new SuperType();//此时SubType.prototype会具有name属性, //这会引起属性共享问题 var s1=new SubType(); var s2=new SubType(); s2.names.push(“cici”);//此时s1的names也改变了
没有很好地解决类型识别问题
function SuperType(){} function SubType(){} SubType.prototype=new SuperType(); var instance=new SubType();
此时instance的constructor指向的是SuperType,而不是SubType。
4、点评
原形链继承,成也共享败也共享
二、借用构造函数继承
通过call跟apply借用超类的构造函数,原理跟使用构造函数创建对象一样。1、借用构造函数继承做法
示例:
function SuperType(name){ this.name=name; this.sayName=function(){alert(name);} } SuperType.prototype.sayHi=function(){alert(“Hi”);} function SubType(){ SuperType.call(this,”Leo”); this.age=18; }
var obj=new SubType();//new调用SubType构造函数,于是SubType函数里面的this
//指向obj。
2、优势
借用构造函数解决了原形链继承的共享问题
借用构造函数的问题:
3、缺点
属性无法共享
如sayName是一个函数,理应所有对象共享,而不是没创建一个对象,就要给这个对象添加一次sayName方法。
超类原型中的属性对子类不可见
例如上面在SuperType的原型中定义了sayHi方法,但是子类是不会有sayHi方法的。
4、点评
借用构造函数解决了原形链的共享问题,但是也导致该共享的属性如:函数,无法共享。所以借用构造函数继承解决了共享问题,又败在共享
三、组合继承(也叫伪经典继承)
1、组合继承做法示例:
function SuperType(){ this.color=["red","blue"]; } SuperType.prototype.getColor=function(){console.log(this.color);}; function SubType(){ SuperType.call(this); } SubType.prototype=new SuperType(); var obj1=new SubType(); var obj2=new SubType(); obj1.color.push("green"); console.log(obj1.color);//输出red,blue,green console.log(obj2.color);//输出red,blue console.log(SubType.prototype.color);//输出red,blue
2、优势
组合继承融合了原形链和借用构造函数之所长,让该共享的属性共享,不该共享的属性不共享。可以说是一种比较理想的实现继承的手段。
3、缺点
虽然组合继承已经是很好的一种继承方式了,但是组合继承还有个小小的遗憾,就是使子类的原型跟子类的实例各自存放一份属性。如上面示例的SubType的原型就有一个color,而obj1和obj2实例本身也有color,于是会有这样的结果,如果delete obj2.color,然后console.log(obj2.color);仍然会输出red,blue,delete后的color来自于SubType原型
4、点评
组合继承集原形链继承及借用构造函数继承之所长,解决了共享问题,让该共享的属性共享,不该共享的属性不共享。虽然还有个小缺点,但是已经算是很理想的继承方式了
四、原型式继承与寄生式继承
所谓原型式继承就是以一个对象A作为另一个对象B的原型,并创建B。所谓寄生式继承,其实就是对原型式继承做了一层简单的封装。1、原型式与寄生式继承做法
示例:
4000
function extend(obj){ function F(){} F.prototype=obj; return new F(); } var Person = { name : '名称', //名称 friends:["Simen","Van"],//引用此属性需要注意原型共享问题 getName : function(){ return this.name; }, }; function createAuthor(name,book){ var author=extend(Person);//原型式继承Person author.name=name; author.book=book; //扩展Person return author; } function createCoder(name,language){ var coder=extend(Person); //原型式继承Person coder.name=name; coder.language=language;//扩展Person return coder; } var author=createAuthor("莎士比亚", "哈姆雷特");//整个createAuthor的过程就是寄生 //式继承 console.log("作者:"+author.getName()+" 代表作:"+author.book);//getName来自 //Person var coder=createCoder("Leo", "C++");//整个createCoder的过程就是寄生式继承 console.log("码农:"+coder.getName()+" 语言:"+coder.language); //getName函数来 //自Person console.log(author.friends);//注意friend是author的原型Person的属性,会有原型共//享问题 console.log(coder.friends); author.friends.push("Lemon"); console.log(author.friends); console.log(coder.friends);
2、优势
可以直接利用现有对象进行扩展,而不必要去兴师动众地声明类
3、缺点
作为原型的对象,最好只有方法,没有属性。如果有属性,最好不要使用属性,否则需要注意原型属性的共享问题。如果共享正好就是你想要的,那就不会有什么问题。
上面的示例代码中,author和coder都以Person对象为原型实现了继承,并各自进行了扩展,author增加了book属性,coder则增加了language属性。由于author和coder的原型都是Person,Person有getName,所以author和coder都可以调用getName。也就是说getName是放在Person中作为原型属性被author和coder共享的,这很好,将函数作为原型属性共享通常是好事。但是注意Person有friends属性,这可能会引起原型共享的问题。所以结论很明显,被继承的对象,这里是Person,最好是只有方法,如果有属性,例如friends,如果需要使用friends的话,需要注意原型共享的问题。
五、寄生组合式继承
前面说过,组合继承有个缺点,就是组合继承调用了两次构造函数,即在子类内部借用构造函数,以及子类的原型以new形式调用超类构造函数,导致子类存有两份超类的实例属性。1、组合继承的缺点:
function SuperType(){ this.name=”Leo”; } SuperType.prototype.sayName=function(){ alert(this.name); } function SubType(){ SuperType.call(this);//继承实例属性 } SubType.prototype=new SuperType();//这里超类的实例与原型属性都继承了,需要注//意的是,上面已经继承过实例属性
2、组合继承的改进方案:寄生组合式继承
function createObjectWithPrototype(obj){ function F(){} F.prototype=obj; return new F(); } function inheritPrototype(SubType,SuperType){ var prototype=createObjectWithPrototype(SuperType.prototype); prototype.constructor=SubType; SubType.prototype=prototype; } function SuperType(){ this.name="Leo"; } SuperType.prototype.sayName=function(){console.log(this.name);}; function SubType(){ SuperType.call(this); this.age=18; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge=function(){ console.log(this.age); }; var s=new SubType(); s.sayAge(); s.sayName();
3、对寄生组合式继承封装
function createObjectWithPrototype(obj){ function F(){} F.prototype=obj; return new F(); } function inheritPrototype(SubType,SuperType){ var prototype=createObjectWithPrototype(SuperType.prototype); prototype.constructor=SubType; SubType.prototype=prototype; } /** *若超类有参数,需要指定第三个参数。影响性能主要因素有超类的参 *数个数,超类参数越多性能越低。如果要传参,最好超类与子类都采 *用参数对象进行传参。另一个影响性能的因素就是在调用extend之 *对子类的原型进行了扩展,扩展越多,性能越低。最好是在extend之 *后再对子类原型进行扩展 */ function extend(SubType,SuperType,subTypeArgsNum){ if(arguments.length<2) throw new Error("参数错误,至少要两个参数"); if(typeof SubType!="function" || typeof SuperType!="function") throw new Error("参数类型错误,第一个参数需要传入function类型"); if(typeof subTypeArgsNum!="number" && subTypeArgsNum!=null) throw new Error("参数类型错误,第三个参数需要传入数值类型或者为空"); function F(SubTypeArgs){//构造函数继承 SubType.apply(this,arguments); if(subTypeArgsNum!=null){ var argLength=arguments.length; var superArgs=[]; for(var i=subTypeArgsNum;i<argLength;i++){ superArgs.push(arguments[i]); } SuperType.apply(this,superArgs); } else SuperType.apply(this); }; inheritPrototype(F, SuperType);//原型继承 var subTypePrototype=SubType.prototype; var prototype=F.prototype; for(property in subTypePrototype){ prototype[property]=subTypePrototype[property]; } prototype.constructor=SubType; return F; } function SuperType(name){ this.name=name; } SuperType.prototype.sayName=function(){console.log(this.name);}; function SubType(ageObj){ this.age=ageObj.age; } SubType.prototype.sayAge=function(){ console.log(this.age); }; SubType=extend(SubType,SuperType,1); var s=new SubType({age:18},"Leo"); s.sayAge(); s.sayName();
将继承过程封装起来后,实现了子类与超类继承的解耦,提高了代码的简洁性。不
过这是以较大的性能损耗作为代价的。还有待改进
4、优势
寄生组合继承,在组合继承的基础上对其进行改进,解决了组合继承的缺点。
可以说是一种近乎完美的继承方案
5、缺点
寄生组合继承的缺点就是比组合继承略微复杂点,不过这不是什么问题。
6、组合、组合寄生与组合寄生封装后的性能比较
组合继承,组合寄生继承与组合寄生封装后的继承性能比较:组合继承与组
合寄生继承性能相当。但是函数封装后实现继承所需时间是则是组合继承或
组合寄生继承所花时间的3~10倍,性能损失很大。但是封装函数实现继承比
较简单,而且由于统一使用继承函数,代码更加简洁可维护,可根据实际情
况选用继承方式
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子
- JavaScript 各种遍历方式详解
- call/apply/bind 的理解与实例分享