JS继承
所有开发者定义的类都可以作为基类,出于安全原因所有本地类,和宿主类 是不能作为基类使用的。
应为js 中所有的属性和方法都是公用的所以子类一旦继承父类就拥有了父类所有的属性和方法,而且可以为父类新增方法和属性,也可以覆盖父类的属性和方法。
因为Emcascript中没有明确的指定继承方式,所有所有的继承都是模拟而来
对象冒充:
对象冒充是开发者在在函数中频繁的使用this关键字后出现的。原理如下:
构造函数使用this给所有的属性和方法赋值,因为构造函数只是一个函数,所有可以利用ClassA 的构造函数成为ClassB的方法,实现代码如下
function ClassA(colora){ this.color=colora; this.showColor=function(){ console.log(this.color); } } function ClassB(colorb){ this.newMethod=ClassA; this.newMethod(colorb); delete this.newMethod; }
这种继承方式也可以模式多重继承,在ClassB函数内 在指向一个新的引用就可以,但是这种方式存在了一个问题就是
当多重继承的时候多个跟类有同样名字的方法或者属性,后继承的会覆盖前继承的。
解疑: 此种形式我刚开始有点不解,为什么通过这种方式B就有color 和showColor 方法我试着用这种形式的代码改造了一下
function ClassB(colorb){ this.newMethod=function(colorb){ this.color=colorb; this.showColor=function(){ console.log(this.color); } } this.newMethod(colorb); delete this.newMethod; }
我感觉这种写法更能说明ClassB 的继承实现原理
Call 方法
call()方法是与对象冒充最相似的方式,在emcascript 改进后function 新增了call() 和apply()两个方法,
在使用call 的时候第一个参数是this ,其他的参数则是直接专递给函数自身例如
function sayColor(prefix,suffix){ console.log(prefix+"__color:"+this.color+"__"+suffix);//这里this就等于call传递过来o } var o=new Object(); o.color="red"; sayColor.call(o,"sb","ub");//这个o就相当于了sayColor 中this
如果是call()的方式改造上一个继承方式如下代码
function ClassB(colorb){ ClassA.call(this,colorb); this.showName=function(){ console.log(this.colorb); } }
Apply方法
apply 有两个参数,第一个是this 要传递的对象,第二个是参数数组
function ClassB(color){ ClassA.apply(this,new Array("red")); this.showName=function(){ console.log(this.color); } }
超类传入的参数数组必须和积累顺序一致。
原型链
prototype 的对象是一个模板,要实例化的对象都是以这个模板为基础,所有prototype 对象的任何属性和方法都会传递给那个类所有的对象原型链使用这个特性实现继承的功能。
function ClassA(color){ ClassA.prototype.color=color; ClassA.prototype.showColor=function(){ console.log(this.color); } } function ClassB(color){ ClassB.prototype=new ClassA(color) } function ClassB(color){}; ClassB.prototype=new ClassA(); var b=new ClassB("red"); b.showColor(); var b=new ClassB(); b.showColor(); console.log(b instanceof ClassA);//true console.log(b instanceof ClassB);//true
使用原型链产生的超类的对象 instanceof 既是基类又是超类对象冒充则没有这个功能,原型链的缺点是不能实现多重继承。而且没法使用构造函数。
混合模式
对象冒充就必须要使用构造函数,但是使用原型链就没法使用构造函数,所以就产生了混合模式
function ClassA(color){ this.color=color; } ClassA.prototype.showColor=function(){ console.log(this.color); } function ClassB(color,name){ ClassA.call(this,color); this.name=name; } ClassB.prototype=new ClassA(); ClassB.prototype.showName(){ console.log(this.name); }
动态原型实现继承
在不采用动态原型时代码
function Polygon(side){ this.side=side; } Polygon.prototype.getArea=function(){ } function Rect(iBase,height){ Polygon.call(this,4); this.iBase=iBase; this.height=height; } Rect.prototype.getArea=function(){ return this.iBase*this.height; }
采用动态原型实现的js 继承代码
function Polygon(side){ this.side=side; if(typeof Polygon._initialized=='undefined'){ Polygon.prototype.getArea=function(){ return 0; } Polygon._initialized=true; } } function Rect(iBase,height){ Polygon.call(this,4); this.iBase=iBase; this.height=height; if(type Rect._initialized=="undefined"){ //Rect.prototype=new Polygon(); 如此定义是错误的 // 代码运行前对象已经被实例化,并与原始对象的prototype 对象 //关联起来,虽然采用后期绑定可以是原型对象的修改正确显示出来,但是替换prototype 对象不会产生任何影响,只有未来实例的对象才能反映出来,第一个对象的实例是不正确的。 Rect.prototype.getArea=function(){ return this.iBase*this.height; } Rect._initialized=true; } } Rect.prototype=new Polygon();