前言
关于JavaScript继承相关的定义和方法网上已经有很多解释啦,本菜鸟就不抄抄写写惹人嫌了,本文主要探讨三种基本的继承方式并且给出优化方案。
正文
-
借助构造函数实现继承
function Parent1() {
this.name = '喵喵喵'; this.arr = [1]; } Parent1.prototype.say = function () { alert('我肯定没被继承,所以弹不出来'); }; function Child1() { Parent1.call(this); // 这里是借助构造函数实现继承的关键 } var t1 = new Child1();
好的,我们来通过控制台看看结果:
可以发现,Child1已经成功继承了Parent1构造函数中个属性,那么问题来了,Parent1原型的属性Child1继承了吗?
答案是并没有!这就是借助构造函数实现继承的缺点,被继承对象原型上的属性不能被继承。
2.借助原型链实现继承
Tips:所有实例对象共享的属性和方法都放在prototype中
function Parent2() {
this.arr = [1];
this.a = '1';
}
Parent2.prototype.say = function () {
alert('终于被继承了');
};
function Child2() {
this.type = '汪汪汪';
}
Child2.prototype = new Parent2(); // 这里是借助原型链实现继承的关键
var s1 = new Child2();
var s2 = new Child2();
我们再次打开控制台看看:
ok!到这里,我们已经成功实现让Parent2中的属性以及它原型链上的属性都被继承。但是这里要注意包含引用类型属性的原型会被所有的实例共享
。啥意思呢?Child2.prototype = new Parent2();
这行代码把Parent2的实例赋给了Child2的原型,而Parent2中存在引用类型this.arr
。
s1和s2本是Child2的两个不同实例,但修改s2.arr同样映射到s1.arr这个问题体现出了借助原型链实现继承的缺点: Parent2中的引用属性被Child2的实例共享了
。
3.组合方式实现继承(原型链 + 构造函数)
function Parent3() {
this.type = '喵喵喵';
this.arr = [1]
}
function Child3() {
this.type = '汪汪汪';
Parent3.call(this); // 与2的唯一区别是多了这一句
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
组合方式实现了Child3对Parent3构造函数内和原型上所有属性和方法的继承,并且Child3的实例对象之间也不会相互干扰。当然啦,厉害的看官已经发现了组合方式的问题:Parent3.call(this)、new Parent3();
在实现组合继承的过程中Parent3执行了两次
,这其实是没有必要的。
- 组合方式实现继承-优化
function Parent4() {
this.type = '喵喵喵';
this.arr = [1]
}
Parent4.prototype.cide = [1,2,3];
function Child4() {
this.type = '汪汪汪';
Parent4.call(this);
}
Child4.prototype = Parent4.prototype;// 优化重点
var s5 = new Child4();
var s6 = new Child4();
这种组合方式的优化方案其实可以分两步来看:
1) Parent4.call(this)
实现对构造函数中的属性和方法的继承
2)Child4.prototype = Parent4.prototype;
实现对构造函数的原型的属性和方法的继承
观察3中的代码我们可以发现,通过Parent3.call(this)
我们规避了Parent构造函数中引用属性对child实例的影响,但如果Parent的原型中存在引用类型(Parent4.prototype.cide),那么在child的实例中修改该属性,同样会映射到其它实例。