《JavaScript》——面向对象之继承

时间:2022-02-07 19:48:36

     继承是面向对象中一个比较核心的概念。 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而 ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。在JavaScript中的继承中,分了好几类继承,可以说是伴随着问题的出现,继承的方法也升级了,不光是原型链继承,还有组合继承、原型继承、寄生式继承、寄生组合继承等等。他们伴随着不同问题的出现而出现,下面我分别介绍一下这几种继承方式。

     1、原型链式继承   

<span style="font-family:KaiTi_GB2312;font-size:18px;">function Box() { //Box 构造
this.name = '美女';
}
function Desk() { //Desk 构造
this.age = 100;
}
Desk.prototype = new Box(); //Desk 继承了 Box,通过原型,形成链条
var desk = new Desk();
alert(desk.age);
alert(desk.name); //得到被继承的属性</span>
     缺点:原型链式继承实例化后的初始值是一样的,不能改变,也无法复用,组合继承(原型链+借用构造函数)来解决这种问题。

     2、组合继承(原型链+借用构造函数)

<span style="font-family:KaiTi_GB2312;font-size:18px;">
function Box(age){
this.name=['林志玲','范冰冰'];
this.age=age;
}

Box.prototype.run=function(){
return this.name+'的年龄是'+this.age;
}

function Desk(age){
Box.call(this,age); //对象冒充,这里的作用是传递参数,只会讲Box的实例中的方法和属性传递给Desk(),原型中的信息不会传递。
}

// var desk=new Desk(20);
// alert(desk.name+'你们好吗???'); //这里返回 林志玲,范冰冰你们好吗??? 原因是Desk()函数内部使用了对象冒充。
// alert(desk.run()); //没有设置原型链继承,run()方法不会被继承下来。

Desk.prototype=new Box(); //使用原型链继承,将Box()的原型和实例的所有信息都传给Desk()
var desk=new Desk(20);
alert(desk.name+'你们好吗???'); //返回 林志玲,范冰冰你们好吗???
alert(desk.run()); //返回 林志玲,范冰冰的年龄是 20
</span>

     组合继承的弥补了原型链式继承穿参和共享问题,但是缺点是每次实例化一个对象的实例,都需要两次调用超类Box().而寄生组合继承就能解决此问题。寄生组合继承一会在说,先说一下原型式继承。

     3、 原型式继承

<span style="font-family:KaiTi_GB2312;font-size:18px;">//
function obj(o) { //传递一个字面量函数
function F() {} //创建一个构造函数
F.prototype = o; //把字面量函数赋值给构造函数的原型
return new F(); //最终返回出实例化的构造函数
}

var box = { //字面量对象
name : 'Lee',
arr : ['哥哥','妹妹','姐姐'] //添加一个数组,数组时引用类型
};

var box1 = obj(box); //传递
alert(box1.name); //返回Lee
box1.name = 'Jack'; //将name改成jack
alert(box1.name); //返回Jack

alert(box1.arr);
box1.arr.push('父母'); //arr属性是一个数组,这里往数组里多添加一个父母。
alert(box1.arr);


var box2 = obj(box); //传递
alert(box2.name); //返回Lee,而不是Jack,子类之间,值类型没有共享。
alert(box2.arr); //返回哥哥,妹妹,姐姐,父母,子类之间,引用类型共享了。</span>
     这种继承借助原型并基于已有的对象创建新对象实例,同时还不必因此创建自定义类型(不用自己添加属性和方法)。

     4、寄生式继承(原型继承+工厂方法模式)

<span style="font-family:KaiTi_GB2312;font-size:18px;">function obj(o){
function F(){}
F.prototype=o;
return new F();
}

function create(o){
f=obj(o)
f.run=function(){
return this.family;
}
return f;
}

var box={
name:'林志颖',
age:22,
family:['引用类型1']
}

var box1 = create(box); //传递
alert(box1.name); //继承的是box对象,输出 林志颖
box1.name='刘亦菲'; //将自己实例中name属性改成刘亦菲,更改的是子类型的name
alert(box1.name); //输出刘亦菲
alert(box1.run()); //输出的是引用类型1,
box1.family.push('引用类型2');//输出的是引用类型1,引用类型2,更改的是超类型的family
alert(box1.run());

var box2 = create(box); //传递
alert(box2.name); //继承的是box对象,输出 林志颖 并没有输出刘亦菲
alert(box2.run()); </span>
     寄生式继承把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。

     5、寄生组合继承 解决多次实例化时,多次调用超类问题。   

<span style="font-family:KaiTi_GB2312;font-size:18px;">//中转函数
function obj(o){
function F(){}
F.prototype=o;
return new F();
}
//寄生函数
function create(box,desk){
f=obj(box.prototype); //这里传的是Box的原型对象
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('美女',25)
alert(desk.run()); //返回美女,25</span>
    小结

    这几种继承方式都介绍完了,我只是了解到皮毛,但是它们在项目中的真正作用哪怕只有在锻炼项目时才会发现,现在说什么都是嘴把式,再有真把式之前,这些理论知识也是很有必要的,就让项目再升华这几种继承方式吧!