ECMAScript支持继承,而且其实现继承的主要是依靠原型链来实现
一、原型链
首先要弄清楚构造函数,原型和实例三者的关系!
构造函数中有一个prototype属性是指向原型的,原型中有一个constructor属性是指向构造函数的(既构造函数和原型对象之间是互相指向对方的),实例对象中有一个[[prototype]]属性是指向原型的。由此可以看出,实例和构造函数之间并没有明确的联系,都是靠原型对象作为两者沟通的桥梁
当在实例(a,A的实例)中找不到某一属性或方法时,它会去实例的原型对象(A.prototype)中寻找,若是这一个原型对象又是某一个构造函数(B)的实例,结果会怎样?
若一个原型对象是另一个函数的实例,那么这一个原型对象肯定包含一个指向另一个函数的原型对象的指针(这里如果绕不清楚的话,多看几遍原型对象),又如果另一个函数的原型对象又是另另外一个函数的实例,这样层层递进,就引出了原型链了!
A继承了B——>A的原型是B的实例,A的原型指向B的原型
常用的判断方法:
(1).instanceof()——构造函数和实例之间是否存在关系,返回Boolean值
(2).isPrototypeOf()——实例与原型对象是否存在关系,返回Boolean值
function SuperType(){
this.name="cwj";
this.colors=["red","black","blue"];
}
function SubType(){
}
subTpe.prototype=new SuperType();//subType的原型继承了SuperType
var instance1=new SubType();
instance1.colors.push("yellow");
alert(instance1.colors);//"red","black","blue","yellow"
var instance2=new SubType();
alert(instance2.colors);//"red","black","blue","yellow"
原型链继承存在的两个问题:
(1)引用类型的问题。通过原型链继承,原先的实例的属性和方法会变成原型的属性和方法,若原先的实例中本来就存在一个引用类型的属性,那么原型也会继承这个引用类型的属性,结果会怎样?该原型的所有实例都会共享这一个引用类型的属性,当修改其中一个实例的这一属性时,另一实例也会反映出来。(就像上图中的colors属性)
(2)创建子类型实例时,不能向超类型传递数据 ,例如我想在创建实例的同时,让name属性的值为“www”,但是不能实现
二,借用构造函数(解决引用类型的问题,还可以传递参数)
function SuperType(){
this.name=name;
this.colors=["red","black","blue"];
}
function SubType(){
SuperType.call(this,"wwww");//继承了SuperType
}
var instance1=new SubType();
instance1.colors.push("yellow");
alert(instance1.colors);//"red","black","blue","yellow"
alert(instance1,name);//"www"
var instance2=new SubType();
alert(instance2.colors);//"red","black","yellow"
用call方法改变SuperType构造函数的执行环境,实际上是在新创建的SubType实例的环境下调用SuperType构造函数,新SubType实例会拥有SuperType函数中定义的所有属性和方法
借用构造函数存在的问题:函数无法复用
三、组合继承(最常使用的方法)
使用原型链实现对原型属性和方法的继承,使用构造函数实现对实例属性的继承
function SuperType(){
this.name=name;
this.colors=["red","black","blue"];
}
function SubType(){
SuperType.call(this,"wwww");//继承了SuperType;第二次调用超类构造方法
}
subType.prototype=new SuperType();//这一步相当于重写了子类的原型,会丢失默认的constructor属性,第一次调用超类构造方法
subType.protoype.constructor=subType;
缺点:组合继承调用了两次超类的构造方法。第一次是创建子类原型时调用,第二次是在调用SubType构造方法时调用
四、原型式继承
在已有原型对象的基础上,创建该原型对象的实例,无需用到构造函数。
function object(o){
function F(){};
F.prototype=o;
return new F();
}
ES5新增了Object.create()方法,规范了原型式继承,第一个参数是原型对象,第二个参数是定义的属性
五、寄生式继承
在原型继承的基础上,利用工厂模式创建对象自己的方法,然后返回这个对象
function createAnother(original){
var clone=object(original);//通过原型模式创建实例对象
clone.sayHi=function(){//创建实例对象的方法
alert("hi");
};
return clone;//返回对象
}
六、寄生组合继承
由于组合继承调用了两次超类的构造函数,所以用寄生式继承代替第一次超类构造函数调用(及代替了原型式继承)。
原型式继承的重点其实是让子类的原型等于超类的实例,这一点寄生式继承也能做到,并且寄生式继承能在没有构造函数的情况下,创建子类,并使子类的原型等于超类的原型
function inheritPropotype(subType,superType){
var prototype=object(superType.prototype);//创建一个实例对象,是以superType的原型为原型的实例对象
prototype.constructor=subType;
subType.prototype=prototype;
}