一、JavaScript实现继承的三层含义:
① 子类的实例可以共享父类的方法;
② 子类可以覆盖父类的方法或者扩展新的方法;
③ 子类和父类都是子类的实例。
二、实现继承的几种方法:
1、原型链继承
例:function Box( ){ //被继承的函数叫超(父)类型;
this.name = “Andy”;
}
function Desk( ){ //继承的函数叫子类型
this.age = 25;
}
Desk.prototype = new Box( ); //将Box构造里的信息和原型信息都给Desk
var desk = new Desk( );
alert(desk.name); //返回 Andy;
Desk.prototype.name = "Jack”; //重写父类型中的属性
var desk2 = new Desk();
alert(desk2.name); //返回 Jack;
ps: ① 如上例,若子类型要重写父类型中某个方法或属性,给原型添加的代码一定要放在替换原型的语句之后。
② 在通过原型链继承时,不能使用字面量创建原型对象,这样会重写原型链
2、对象冒充法(即 借用构造函数)
解决引用共享和超类型无法传参的问题,采用“对象冒充”技术继承。
例:function Box (name,age){
this.name = “Andy”;
this.age = 25;
this.family = [“姐姐”,“哥哥”,“妹妹”];
}
function Desk(name,age){
Box.call(this,name,age) //Desk 冒充 Box
}
var desk = new Desk( );
alert(desk.name); //返回 Andy
alert(desk.family); //返回 姐姐,哥哥,妹妹
PS:
① call和apply方法都可以实现继承,两者的第二个参数不同,call是一个个的参数列表
apply的第二个参数是数组。
例:Box.call(this,name,age);
Box.apply(this,[name,age]);
② 对象冒充只能继承构造里的信息
例:接上例添加:
Box.prototype.family = “家庭”;
alert(desk.family); //继承不了,仍然返回 姐姐、哥哥、妹妹
3、组合继承法(对象冒充+原型链继承方式)
使用原型链实现对原型属性和方法的继承,使用对象冒充来实现对实例属性的继承
例:function Box(name,age){
this.name = name;
this.age = age;
}
Box.prototype.work = function(){
return this.name+this.age+”运行中”;
}
function Desk(name,age){
Box.call(this,name,age) //对象冒充,构造里继承
}
Desk.prototype = new Box( ); //原型链继承
var desk = new Desk(“Andy”,25);
alert(desk.work( ) ); //返回 Andy 25 运行中
小 结
① 原型链继承中,包含引用类型值的原型的原型属性会被所有实例共享,并且创建子类型的实例时,不能向超类型的构造函数中传递参数,因此实践中很少会单独使用原型链。
② 对象冒充(借用构造函数法)中,可以通过call()或者apply()方法在新创建的对象上实行构造函数,解决了原型链继承法中共享和无法传参的问题。但对象冒充只能继承构造里的信息,会导致函数无法复用。
③ 组合继承中,使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承,既能实现函数复用,又能保证每个实例都有自己的属性,因此,成为JavaScript常用的继承模式(唯一问题:超类型会被调用两次)
4、原型式继承(与原型链继承类似)
例://临时中转函数
function obj(o){ //o表示将要传递进入的一个对象
function F( ){ } // 临时新建的对象,用来储存传递过来的对象
F.prototype = o; //将o对象实例赋值给F构造的原型对象
return new F( );
}
var box = { //相当于 var box = new Box();
name:”Andy”,
age:28,
family:[“哥哥”,“姐姐”]
}
var box1 = obj (box); //相当于 var box1 = new F();
alert(box1.name); // 返回 Andy
alert(box1.family); // 返回 哥哥,姐姐
box1.family.push(“弟弟”);
alert(box1.family); // 返回 哥哥,姐姐,弟弟
var box2 = obj(box);
alert(box2.family); // 返回 哥哥,姐姐,弟弟
因此,原型式继承的问题是包含引用类型值的属性始终会共享相应的值
5、寄生式继承(原型式 + 工厂模式)
例:function obj(o){ //与原型式继承相同,临时中转函数
function F(){}
F.prototype = o;
return new F();
}
function create(o){ // 创建寄生函数
var f = obj(o);
f.run = function(){
return this.name+"方法";
}
return f ;
}
var box = {
name:”Andy",
age:100,
family:["哥哥","姐姐"]
};
var box1 = create(box);
alert(box1.name) //Andy
alert(box1.run()); //Andy 方法
此方法可以为对象添加函数,但也不能做到函数复用,与借用构造函数继承类似。
6、寄生组合式继承
function obj(o){
function F( ){};
F.prototype = o;
return new F( );
}
function create(box,desk){
var f = obj(box.prototype);
f.constructor = desk; //调整原型构造指针
desk.prototype = f;
}
function Box(name,age){
this.name = name;
this.age = age;
}
Box.prototype.run=function(){
return this.name+this.age +”启动”;
}
function Desk(name,age){
Box.call(this,name,age); //对象冒充
}
create(Box,Desk); //替代 Desk.prototype = new Box( );
var desk = new Desk(“Andy”,28);
alert(desk,run()); //弹出 Andy28启动
问题:组合继承是 js 最常用的继承方式,但组合继承的超类型会在使用过程中会被调用两次,一次在创建子类型时,一次在子类型构造函数内部,即:Desk.prototype = new Box( );第一次调用;Box.call(this,name,age);第二次调用。
解决:寄生组合式继承则解决了超类型被调用两次的问题