前言: 写到这里,差不多就把OOP完结了,写了几篇OOP的文章,但是只是略懂皮毛,可能深入的OOP还有很多,但是我感觉写到这里也算是差不多完结了。
继承
继承是面向对象比较核心的概念,其他语言可能实现继承有两种方式:接口,继承。而ECMAscript只有继承。
原型链继承
通俗的讲,就是通过构造函数的原型对象去继承父类。上一篇文章我们说过。继承最大的优点也是缺点就是共享,看代码:
function First() { //创建一个构造函数(作为父类)
this.a = 'abc'; //添加属性
}
function Second() { //另一个构造函数(作为派生类也称为子类)
this.b = '10'; //同上
}
Second.prototype = new First(); //子类的原型对象添加父类的实例。
var box = new Second(); //实例化并赋值给一个变量
alert(box.a); //返回abc
大家看的出,第二个构造函数内并没有a这个属性,但是我们从原型去添加一个父类的实例化,就能继承父类的构造体内的和原型对象。
Second构造函数通过原型继承了First构造函数和原型,这就形成了一个链条,通俗的讲就是:原型链继承。
也可以这么说,用对象实例化赋值给子类的原型属性,会将父类的构造函数里面的信息和原型里面的信息都交给子类。
重谈原型:
还是那句话,字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。
对象冒充
不知道大家发现没有,父类是无法传参的,而且引用共享的问题,我们可以使用对象冒充的方法去实现继承。
function First (b) { //创建一个构造函数并传参
this.a = ['a','b','c']; //添加一个属性为一个数组
this.b = b; //添加属性得到传进去的值
}
function Second (b) { //同上
First.call(this,b); //对象冒充
}
var second = new Second(100); //实例化子类并赋值给变量
alert(second.a); //返回a,b,c
alert(second.b); //返回100
从上面的代码我们可以看出,我们解决了父类传参问题,也解决了引用共享问题。但是没有原型。而且call方法只能冒充构造函数里面的属性和方法而无法冒充原型对象里面的属性和方法
还有最大的问题就是重复使用。
我们来用代码证明上面的话,就是call冒充只是冒充构造函数里面的方法和属性。
/*alert(second.a); 返回a,b,c */
second.a.push('d'); //给数组末尾添加一个d
alert(second.a); //返回a,b,c,d
从上面代码看出,并没有继承原型,也就是共享。重复使用更是不可能的。
原型链+冒充
当然我们可以变通一下,将上面两种方法结合一下不就达到效果了么!我们大概的思想是该私有化的属性和方法就私有化,该共享的公有化的方法就放在原型里面不就得了!
function First (b) {
this.a = ['a','b','c'];
this.b = b;
}
First.prototype.c = function () { //公有化方法放在原型中
return this.a + this.b;
};
function Second (b) {
First.call(this,b); //对象冒充
}
Second.prototype = new First(); //子类的原型对象添加父类的实例
var second = new Second(100); //实例化并赋值给变量
alert(second.c()); 返回a,b,c10
从上面的例子用可以看出,我们将我们不想共享的属性和方法放在构造函数中,而我们想私有化的方法放在原型对象中,达到了清晰代码的作用,而且解决了重复使用和传参的问题并且有了原型!
总结:
说了这么多,其实Javascript中实现继承是十分灵活的,并没有一种最好的方法,需要根据不同的需求实现不同方式的继承,最重要的是要理解Javascript中实现继承的原理,也就是原型和原型链的问题,只要理解了这些,自己也就可以很轻松实现继承了。
Brian.Lee
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。