面向对象与原型(三)
继承:
继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继
承: 一个是接口实现, 一个是继承。 而ECMAScript只支持继承, 不支持接口实现,而实
现继承的方式依靠原型链完成。
function Box(){ //Box构造
this.name='Lee';
}
function Desk(){ //Desk构造
this.age=100;
}
Desk.prototype=new Box(); //Desc继承了Box, 通过原型, 形成链条
var desk=new Desk();
alert(desk.age);
alert(desk.name); //得到被继承的属性
function Table(){ //Table构造
this.level='AAAAA';
}
Table.prototype=new Desk(); //继续原型链继承
var table=new Table();
alert(table.name); //继承了Box和Desk
原型链继承图
在JavaScript里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为
子类型(子类,派生类)。继承也有之前问题,比如字面量重写原型会中断关系, 使用引用类型的原型
,并且子类型还无法给超类型传递参数。为了解决引用共享和超类型无法传参的问题,我们采用一种
叫借用构造函数的技术,或者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。
function Box(age){
this.name=['Lee','Jack','Hello']
this.age=age;
}
function Desk(age){
Box.call(this,age); //对象冒充,给超类型传参
}
var desk = new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push('AAA'); //添加的新数据,只给 desk
alert(desk.name);
借用构造函数虽然解决了刚才两种问题, 但没有原型, 复用则无从谈起。 所以, 我们需要
原型链+借用构造函数的模式,这种模式成为组合继承。
function Box(age) {
this.name = ['Lee', 'Jack', 'Hello']
this.age = age;
}
Box.prototype.run = function () {
return this.name + this.age;
};
function Desk(age) {
Box.call(this, age); //对象冒充
}
Desk.prototype = new Box(); //原型链继承
var desk = new Desk(100);
alert(desk.run());
还有一种继承模式叫做:原型式继承;这种继承借助原型并基于已有的对象创建新对象,
同时还不必因此创建自定义类型。
function obj(o) { //传递一个字面量函数
function F() {} //创建一个构造函数
F.prototype = o; //把字面量函数赋值给构造函数的原型
return new F(); //最终返回出实例化的构造函数
}
var box = { //字面量对象
name : 'Lee',
arr : ['哥哥','妹妹','姐姐']
};
var box1 = obj(box); //传递
alert(box1.name);
box1.name = 'Jack';
alert(box1.name);
alert(box1.arr);
box1.arr.push('父母');
alert(box1.arr);
var box2 = obj(box); //传递
alert(box2.name);
alert(box2.arr); //引用类型共享了
寄生式继承把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。
function create(o) { //封装创建过程
var f= obj(o);
f.run= function () {
return this.arr; //同样,会共享引用
};
return f;
}
组合式继承是 JavaScript最常用的继承模式; 但, 组合式继承也有一点小问题, 就是超
型在使用过程中会被调用两次: 一次是创建子类型的时候, 另一次是在子类型构造函数的部。
function Box(name) {
this.name = name;
this.arr = ['哥哥','妹妹','父母'];
}
Box.prototype.run= function() {
returnthis.name;
};
function Desk(name, age) {
Box.call(this, name); //第二次调用 Box
this.age = age;
}
Desk.prototype = newBox(); //第一次调用 Box
以上代码是之前的组合继承,那么寄生组合继承,解决了两次调用的问题。
function obj(o) {
functionF() {}
F.prototype= o;
return new F();
}
function create(box,desk){
var f=obj(box.prototype);
f.constructor=desk;
desk.prototype=f;
}
function Box(name){
this.name=name;
this.arr=['哥哥','妹妹','父母'];
}
Box.prototype.run=function(){
return this.name;
};
function Desk(name,age){
Box.call(this,name);
this.age=age;
}
create(Box,Desk); //通过这里实现继承
var desk=new Desk('Lee',100);
desk.arr.push('姐姐');
alert(desk.arr);
alert(desk.run()); //只共享了方法
var desk2=new Desk('Jack',200);
alert(desk2.arr); //引用问题解决