javascript构造函数的继承简单介绍
实现继承的方式有多种,下面介绍一下比较常见的几种方式,需要的朋友可以做一下参考。
准备工作:
下面是一个父类,当前ES版本中还没有类这个概念(ES6新增class),不过确实是类的作用:
function Web(){ this.target="用户浏览"; }
下面是一个子类:
function Antzone(webName,url){ this.webName=webName; this.url=url; }
那面就介绍一下如何让子类来继承父类中的相关属性。
一.构造函数绑定:
使用call()或者apply()方法可以实现此功能。
所谓的构造函数绑定,就是将父类构造函数绑定到子类对象上,代码实例如下:
function Web(){ this.target="用户浏览"; } function Antzone(webName,url){ Web.apply(this,arguments); this.webName=webName; this.url=url } var oantzone=new Antzone("前端教程网","pipipi.net"); console.log(oantzone.target);
二.使用prototype原型实现继承:
如果子类Antzone的原型指向父类的对象实例,那么子类的对象实例将会继承父类的所有属性和方法。
代码实例如下:
function Web(){ this.target="用户浏览"; } function Antzone(webName,url){ this.webName=webName; this.url=url } Antzone.prototype=new Web(); var oantzone=new Antzone("前端教程网","pipipi.net"); console.log(oantzone.target);
上面的代码实现了继承功能,但是这并不完美,甚至可以说是有问题的。
Antzone.prototype=new Web()是覆盖了子类的原型对象,那么这个时候Antzone.prototype.constructor指向就不是Antzone,而是Web,那么对象实例antzone的constructor就不会指向Antzone了,这样就会导致继承链的紊乱,代码修改如下:
function Web(){ this.target="用户浏览"; } function Antzone(webName,url){ this.webName=webName; this.url=url } Antzone.prototype=new Web(); Antzone.prototype.constructor=Antzone; var oantzone=new Antzone("前端教程网","pipipi.net"); console.log(oantzone.target); console.log(oantzone.constructor==Antzone);
上面的代码重新修改了一下Antzone.prototype.constructor的指向即可。
三.直接继承prototype:
上面代码是继承的父类的对象实例,我们也可以直接继承父类的原型对象,这样的话能够节省内存空间提高效率。
父类中不变的属性可以写在在prototype原型对象中,代码修改如下:
function Web(){ } Web.prototype.target="用户浏览"; function Antzone(webName,url){ this.webName=webName; this.url=url } Antzone.prototype=Web.prototype Antzone.prototype.constructor=Antzone; var oantzone=new Antzone("前端教程网","pipipi.net"); console.log(oantzone.target);
这种方式虽然效率比较高,但是也有一个比较严重的问题,那就是一旦修改子类原型对象,那么父类的原型对象也会被修改,因为实质上它俩是指向的同一个原型对象,代码演示如下:
function Web(){ } Web.prototype.target="用户浏览"; function Antzone(webName,url){ this.webName=webName; this.url=url } Antzone.prototype=Web.prototype Antzone.prototype.constructor=Antzone; Antzone.prototype.target="div教程"; console.log(Antzone.prototype.target); console.log(Web.prototype.target);
四.使用空对象进行过渡:
对于上面方式的缺点,下面就进行一下改进,使用一个空对象进行一下过渡。
代码实例如下:
function Web(){ } Web.prototype.target="用户浏览"; function Antzone(webName,url){ this.webName=webName; this.url=url } var F=function(){}; F.prototype=Web.prototype; Antzone.prototype=new F(); Antzone.prototype.constructor=Antzone; var oantzone=new Antzone("前端教程网","pipipi.net"); console.log(oantzone.target);
声明了一个空函数F(),是为了让它足够的"干净",也几乎不占任何内存空间。
修改Antzone的原型的时候也不会影响到父类Web的原型。
代码封装如下:
function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }
extend函数,就是YUI库如何实现继承的方法。
可能其他代码大家都比较明白了,唯独对最后一行代码可能还有点疑问:
Child.uber = Parent.prototype;
为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。
(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。