继承
A.继承是面向对象中一个比较核心的概念。
B.正统面向对象语言有两种方式实现继承:
(1)接口实现;
(2)继承
C.在ECMAScript只支持继承,不支持接口实现,
而实现继承的方式依靠原型链完成
1、继承方式1——通过原型链实现
(1)被继承的函数——“超类型”(父类、基类)
(2)继承的函数——“子类型”(子类、派生类)
作用:通过原型链继承,可将“超类型”实例化后的对象实例,赋值给“子类型”的原型属性
//被继承的函数叫做超类型(父类、基类)
function Box(){
this.name='Lee';
}
Box.prototype.name='Jack';
//继承的函数叫做子类型(子类、派生类);
function Desk(){
this.age=100;
}
//通过原型链继承,超类型实例化后的对象实例,赋值给子类型的原型属性
//new Box()会将Box构造的信息和原型信息交给Desk
Desk.prototype=new Box(); //Desk的原型得到
//Table.prototype=new Desk();
var desk=new Desk();
alert(desk.name); //遵循就近原则,实例中有,就返回,没有就去查找原型
//var table=new Table();
//alert(desk.age); //100
//alert(desk.name); //Lee
//子类型从属于自己或它的超类型
alert(desk instanceof Object); //true
alert(desk instanceof Desk); //true
alert(desk instanceof Box); //true
alert(box instanceof Desk); //true
上述案例代码中,子类型从属于它的超类型,
同时,在进行变量属性查找时,遵循“就近原则”:
——即先在实例化对象中查找,若有,就返回该属性赋值;若无,就继续查找原型中的该属性。
2、继承方式2——借用构造函数/使用对象冒充继承
目的:解决引用共享和超类型无法传参
作用:实现伪造对象,经典继承。
步骤1:先创建一个构造函数,实例化对象(有this标识)
步骤2:再创建一个构造函数,只作为“对象冒充”使用
a、所谓“对象冒充”,即只继承构造函数中的信息。
例如:
Box.call(this,name,age)
b、对象冒充,不能继承原型函数的信息。
例如:
Box.prototype.family='家庭';
alert(desk.family); //undefined,不能继承原型中的;
步骤3:为构造函数添加格外的信息,并返回。
//1.创建一个构造函数;
function Box(name,age){
this.name=name;
this.age=age;
this.family=['哥哥','姐姐','妹妹'];
//当引用类型,放到构造函数,就会被共享;
}
//Box.prototype.family='家庭';
//2.创建一个构造函数用于对象冒充
function Desk(name,age){
Box.call(this,name,age) //对象冒充,对象冒充只能继承构造函数中的信息;
}
//3.传递参数;
var desk=new Desk('Lee',100);
alert(desk.name); //Lee
//alert(desk.family); //undefined,不能继承原型中的;
alert(desk.family); //哥哥、姐姐、妹妹
desk.family.push('弟弟');
alert(desk.family); //哥哥、姐姐、妹妹、弟弟
var desk2=new Desk('Lee',100);
alert(desk2.family);
3、继承方式3——组合继承
(1)概念:组合继承=原型链+借用构造函数
由于没有原型,则实现不了复用,必须使用组合继承
(2)作用:实现伪造函数、分享、复用
实现过程步骤如下——
步骤1:构造函数实例化对象
function Box(name,age){}
步骤2:为构造函数的原型属性添加方法
Box.prototype.run=function(){}
步骤3:采用对象冒充
Box.call(this,name,age);
步骤4:将实例化方法放在构造函数对象的原型里
Desk.prototype=new Box(); //原型链继承,只继承原型中的;
步骤5:调用Box(),并赋值。
//1、构造函数实例化对象
function Box(name,age){
this.name=name;
this.age=age;
this.family=['哥哥','姐姐','妹妹'];
}
//2、原型中
Box.prototype.run=function(){
return this.name+this.age;
};
//3、构造函数中的方法,放在构造里,每次实例化,都分配一个内存地址,浪费,所以最好放在原型里
function Desk(name,age){
//第二次调用Box
Box.call(this,name,age) //对象冒充
}
//第一次调用Box
Desk.prototype=new Box(); //原型链继承,只继承原型中的;
var desk=new Desk('Lee',100);
alert(desk.run()); //返回null,无值
4、继承方式4——原型式继承
(1)先创建一个临时中转函数,
function obj(o){}
(2)采用字面量方式创建对象
var box={
...
};
(3)为对象赋值并进行调用
//临时中转函数
function obj(o){ //o表示将要传递进入的一个对象;
function F(){ } //F构造是一个临时新建的对象,用来存储传递过来的参数;
F.prototype=o; //将o对象实例赋值给F构造的原型对象;
return F(); //最后返回这个得到传递过来对象的对象实例;
}
//F.prototype=o,其实相当于Desk.prototype=new Box();
//这是字面量的声明方式,相当于var box=new Box()
var box={
name:'Lee';
age:100;
family:['哥哥','姐姐','妹妹'];
};
//box1=new F()
var box1=obj(box);
alert(box1.family);
box1.family.push('弟弟');
alert(box1.family);
var box2=obj(box);
alert(box2.family); //引用类型的属性共享了
(1)概念:寄生式继承=原型+工厂模式
(2)目的:为了封装继承对象
(3)实现流程见步骤——
步骤1:创建临时中转函数
function obj(o){...}
其中o为临时传递的对象,创建的函数function F(){}为临时函数,
步骤2:创建寄生函数
function create(o){}
步骤3:字面量方式创建对象
var box={};
步骤4:调用寄生函数,并赋值
//临时中转函数
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:'Lee',
age:100,
family:['哥哥','姐姐','妹妹'];
};
var box1=create(box);
alert(box1.run());
6、继承方式6—— 寄生组合继承
由于之前的组合式继承(最常用),但是对于超类型在使用中会被调用两次
一次是创建子类型的时候,另一次是在子类型构造函数的内部;
为解决此问题,所以采用寄生组合继承
步骤1:创建临时中转函数
function obj(o){...}
其中o为临时传递的对象,创建的函数function F(){}为临时函数,
步骤2:创建寄生函数
function create(o){}
步骤3:构造函数方式创建实例化对象
function Box(){...}
步骤4:为对象添加原型的方法
Box.prototype.run=function(){}
步骤5:对象冒充方式创建另一个对象
function Desk(name,age){
Box.call(this,name,age); //对象冒充
}
步骤6:采用“寄生组合方式”实现继承
create(Box,Desk);
步骤7:用构造函数方法创建对象,并赋值;
var desk=new Desk('Lee',100);
//临时中转函数
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;
//return 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);
var desk=new Desk('Lee',100);
alert(desk.run());
//Lee 100 运行中...
alert(desk.constructor);
//function Desk(name,age){this.name+this.age;}