ECMAScript中函数没有没签名,所以无法实现接口继承(只继承方法签名),只支持实现继承(继承实际的方法),实现继承主要依靠原型链。
①基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法;
继承是通过创建一个父类的实例,并将该实例赋值给子类的内部属性[[prototype]]实现的。原来存在于父类实例中的所有属性和方法,也存在于子类的prototype属性中。P163页(例子)
②默认的原型:所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向object.prototype,所以所有的引用类型都默认继承了Object;
③确定原型和实例的关系的方法
1)instanceof操作符:测试实例与原型链中出现过的构造函数
alert(p1 instanceof Object); //true
alert(p1 instanceof Person); //true
2)isPrototypeOf(),测试原型链中出现过的原型;
alert(Object.prototype.isPrototypeOf(p1)); //true
④子类型如果需要重写超类型中的某个方法或添加新方法,则必须在用超类型的实例替换原型之后,才能重写或添加方法;
子类型重写某方法后,通过子类实例调用该方法时,调用的是重写后的方法,而父类型的实例调用该方法,则还是重写前的方法;
⑤通过原型链实现继承时,不能使用对象字面量创建原型方法,防止原型链被重写,父类型和子类型之间的联系被切断;
⑥原型链的问题
问题一:在通过原型实现继承时,原型实际上变成另一个类型的实例,原型的实例属性变成现在子类型的原型属性,从而子类型的所有实例可以共享父类型中的所有属性和方法;
function superType(){
this.color=["red","green","blue"];
}
function SubType(){}
//继承SuperType
SubType.prototype=new SuperType();
var instance1=new SubType();
instance1.color.push("black");
alert(instance1.color); //"red,green,blue,black"
var instance2=new SubType();
alert(instance21.color); //"red,green,blue,black"
问题而:在创建子类型的实例时,不能像超类型的构造函数传递参数
所以在实践中很少但是使用原型链实现继承。
借用构造函数也叫伪造对象或经典继承,解决引用类型值所带来的问题。
①基本思想:通过call()或apply()方法在子类型的构造函数中调用超类型的构造函数;
②可以在子类型的构造函数中向超类型构造函数传递函数;
function SuperType(name){
this.name=name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this,"amy");
//实例属性
this.age=23;
}
var instance=new SubType();
alert(instance.name); //amy
alert(instance.age); //23
③借用构造函数的问题
与构造函数模式一样,方法都在构造函数中定义,因此函数的复用就无从谈起了,所以很少单独使用;
组合继承又叫伪经典继承,是js中最常用的继承模式。
①基本思想:使用原型链实现随原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
②优点:既通过在原型上定义方法实现了函数的复用,又能保证每个实例都有它自己的属性。
③缺点:无论什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,另一次是在子类型构造函数内部;
function SuperType(name){
this.name=name;
this.color=["red","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name); //第二次调用SuperType()
this.age=age;
}
//继承方法
SubType.prototype=new SuperType(); //第一次调用SuperType()
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance1=new SubType("amy",23);
instance1.color.push("blue");
alert(instance1.color); //"red,green,blue"
instance1.sayName(); //amy
instance1.sayAge(); //23
var instance2=new SubType("grey",27);
alert(instance2.color); //"red,green"
instance2.sayName(); //grey
instance2.sayAge(); //27
有图可知,有两组name和color属性,一组在实例中,一组在SubType原型中,这就是两次调用SuperType构造函数的结果;
不同的实例既分别拥有自己的属性,又可以使用相同的方法
①基本思想:借助原型可以基于已有的对象创造新对象,同时还不必因此创建自定义类型;
②ES5使用Object.create()方法规范化了原型式继承
var person={
name:"amy",
friend:["coco","rudy"]
};
var anoperson=Object.create(person);
anoperson.firend.push("rob");
var yetperson=Object.create(person,{name:{value:"lala"}});
yetperson.firend.push(linda);
alert(person.friend); //"coco,rudy,rob,linda"
alert(yetperson.name); //"lala"
在没有必要兴师动众的创建构造函数的,只想让一个对象与另一个对象保持类似的情况下,原型继承时完全可以胜任的,但是该继承方式还是跟原型模式一样,包含引用类型值的属性始终都会共享相应的值;
①基本思想:思路与寄生构造函数和工厂模式类似,创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象;
②适用于此在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式的继承也是一种有用的模式。任何能够返回新对象的函数都适用于此模式。
③使用寄生式继承来为对象添加方法,会由于不能做到函数复用而降低效率;
①含义:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
②基本思想:不必为了指定类型的原型而调用超类型的的构造函数,我们所需的无非就是超类型原型的一个副本而已,本质上,就是使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型;
③其高效率体现在它只调用了一次父类型构造函数,并且避免了在子类型的原型中创建不必要的、多余的属性。而原型链还能保持不变,开发人员普遍认为这是引用类型最理想的继承方式
function object(o){
function F(){}
F.prototype=o;
return new F();
}
function inheritPrototype(subtype,supertype){
var prototype =object(supertype.prototype); //创建对象
prototype.constructor=subtype; //增强对象
subtype.prototype=prototype; //指定对象
}
function SuperType(name){
this.name=name;
this.color=["red","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name); //调用SuperType()
this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance1=new SubType("amy",23);
instance1.color.push("blue");
alert(instance1.color); //"red,green,blue"
instance1.sayName(); //amy
instance1.sayAge(); //23
var instance2=new SubType("grey",27);
alert(instance2.color); //"red,green"
instance2.sayName(); //grey
instance2.sayAge(); //27