继承:其本质上是为了复用父类代码,进行扩展,本着这点实现继承在js有多种方式
一 原型链继承
1.1 示例
定义一个基类
function baseType(){};
function SubType(){
//原型继承
SubType.prototype=new baseType();
// 更改构造,表明子类的构造,不然构造会指向baseType在做子类实例检测的时候不能检测出该实例是SubType
SubType.construct=SubType;
}
就这样子类中便可以使用父类中存在的方法和属性了,这点从原型的相关概念便可以轻松知道
1.2 使用场景
父类中只含有方法,原因在于原型模式的优缺点,如果使用原型链继承,那么子类的所有实例都享有公有的原型对象,而原型对象又为基类的一个示例,那么自然的改变原型中的属性大家再去访问就是一致的
子类中再次定义方法时如果与父类同名则覆盖
1.3 优缺点
优点:简单强大;
缺点:跟原型模式的缺点差不多,就是最好不要在父类中有属性,原型链继承不能给向基于构造模式的基类传递参数,这个缺点比较大
二 借用构造函数继承
2.1 示例
function SubType(){
SuperType.call(this,构造函数参数);
//也可以使用apply,不同之处在于apply第二个参数为数组
SuperType.apply(this,argumens);
}
可以简单的理解为将子类实例的this传入父类构造函数,那么子类相应的就拥有了父类的属性和方法,相当于直接把父类进行扩展。此处不用显示的改变构造函数construct
2.2 适用场景
由于本身是基于构造模式扩展类型,相应的跟构造函数模式使用场景相似,即基类中最好不存在原型方法的扩展,不然就是像构造函数模式一样浪费原型空间
2.3 优缺点
优点:可以向基类的构造函数传递参数
缺点:自带构造函数模式天生缺陷,浪费内存
三 组合继承
3.1 简单理解
所谓组合可以先参考组合构造和原型创建对象模式,即兼顾有构造的参数和属性封装优势,以及原型的方法共享优势
3.2 示例
function BaseType(name,id){
this.name=name;
this.id=id;
}
function SubType(){
//借用构造继承,将基类的属性继承过来
BaseType.call(name,id);
}
//继承基类的原型,继承基类的方法
SubType.prototype=new BaseType();
// 修改构造函数
SubType.construct=SubType;
3.3 适用场景
和组合模式创建对象相似,因此组合继承也就成为了经典继承
3.4 原型检测
isPrototypeOf(obj)
如例所示:
var re = /^s*/;
// 这里定义一个正则表达式对象
// 这里检查RegExp是不是re的原形链对象,返回true
var bIsptt = RegExp.prototype.isPrototypeOf(re); //正则表达式类型的原型自然是re正则表达式的原型对象,反过来说:re的原型链中包含 该原型没有?
3.5 优缺点
组合继承兼顾了借用构造和原型链继承的优势,但是他也并不是没有缺点,在子类中指定原型时候调用一次基类构造函数,在子类构造函数中使用借用构造函数继承又调用了一次基类构造函数,对于不希望在构造函数中被多次调用的时候,这个缺点就显得比较突出
四 原型继承
4.1 简单理解
原型继承是指在已有对象基础上,将已有对象原型作为子类原型,有点绕
4.2 示例
var obj={
name='111',
say=function(){
alert(this.name);
}
}
继承: function object(o){
function fn(){};
fn.prototype=o;
return new fn();
}
使用:
//达到简单继承
var obj2=object(obj);
obj2.say();
在es5中直接使用Object.create(obj)等同于object(obj);//新版的es将其纳入到Object的原型上去了
4.3 适用场景
将已有的对象进行扩展,但是该对象一般来说是使用字面量方式构建的,因此没有类型一说,也就无法使用typeof检测类型
4.4 优缺点
跟字面量创建对象一样简单粗暴,临时有效;缺点没有类型,适合小规模小打小闹,临时用下还是可以
五 寄生继承
5.1 简单理解
跟原型继承类似,基本上可以说是在原型继承基础上进行扩展
5.2 示例
function SubType(o){
var obj2=object(o);//此过程可以省略
//扩展
obj2.showMe=function(){
alert(this.name);
}
return obj2;
}
5.3 适用场景
对已存在的对象一种扩展,比较方便与如工厂模式或字面量模式的对象继承
5.4 优缺点
简单粗暴,临时有效
六 强大的寄生组合继承
6.1 简单理解
组合继承可以说能够满足90%的继承需求,但是有一个相对明显的缺点那就是2次调用基类构造函数,因此诞生了寄生组合继承,来避免2次调用基类构造函数
6.2 示例
//先要寄生,寄生原型,避免调用构造
function object(o) {
function F() { }
F.prototype = o;
return new F();
}
function inheritPrototype(subClass, superClass) {
var prototype = object(superClass.prototype);
prototype.constructor = subClass;
subClass.prototype = prototype;
}
//再要构造
function SubClass(){
//基类构造,因此只有此处调用了构造
superClass.call(this);
}
//寄生调用
inheritPrototype(subClass, superClass);
这样就避免了强大的组合继承的缺点,可以说寄生组合是最完美的一种继承方式
6.3 适用场景
适用构建强大复杂类型的类,比如一些客观存在的类型,比如百度地图api中一些类,BMap.Point,BMap.Marker继承自BMap.Overlay
6.4 优缺点就不用说了
七 js中多态性
7.1 简单理解
虽然js中并没有多态的原始支持,但是从变量,方法覆盖一说中可以模拟
7.2 实现
如基类中已经存在某个属性,而子类中再次定义则将覆盖基类中的属性,类似的方法也是一样,因为js对成员的访问机制,先搜索实例本身,再搜索原型直到顶端或者找到
八 调用基类的方法
8.1 简单理解
既然继承了基类,那么想调用基类的方法怎么办?
8.2 实现
Object.getPrototypeOf(
this
).method1();//其中method1是基类的方法,注意如果在子类的覆盖函数中调用则无效,这也是js一大痛点吧,不想c#直接在override中使用base.方法名()