继承是面向对象中一个比较核心的概念。 其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而 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>小结
这几种继承方式都介绍完了,我只是了解到皮毛,但是它们在项目中的真正作用哪怕只有在锻炼项目时才会发现,现在说什么都是嘴把式,再有真把式之前,这些理论知识也是很有必要的,就让项目再升华这几种继承方式吧!