javascript 继承学习心得总结(2)

1、构造函数,js好像没有严格的构造函数的定义,但是可以用new来创建新的对象。构造函数据说也是严格的面向对象的语言实现继承的方法,那么js当然可以模拟一下了,所以学过oop语言的人们会最先想到这个。

2、利用函数原型,利用原型链把两个对象链接起来,因为js原型链是比较独特所以想到这个也是很容易的。
原型也分几种,就是把什么作为继承对象的原型,被继承对象的原型或者被继承对象的实例,或者直接被继承者。这几种作为继承对象的原型得到的继承效果是不一样的。

3、复制属性和方法,把被继承对象的属性或者方法统统复制克隆过来变成我们自己对象的属性和方法就好了啊,我们就可以无比顺畅的用了。当然这个还分浅复制和深度复制两种情况。

4、利用call和apply这两个方法,这两个方法比较神奇,可以改变函数执行的上下文(this),所以利用这两个方法也可以实现对被继承对象的方法的继承复用。
 
总的来js实现继承的途径大概就是这些,千变万化的实现方法都是从这几种方法的基础上组合升级完善出来的,为毛大多数要组合使用呢,当然是因为使用单个方法实现的效果不太理想啊。当然可以根据自己项目中实际的需求选择使用哪种方式,只要满足自己的需求并没有说必须使用哪种方法去实现。就像说从北京去石家庄,最快当然是飞机啦。但是如果离机场远,算上到机场,从机场去市里,整体算下来还不如高铁快,那就可以做高铁。又比如自己有车可以开车,想要挑战一下还可以骑自行车,这个根据自己的实际情况来选就可以。


代码实现,下面结合代码说说上面的几种实现方法,有些是从其他地方摘来的,加点儿注释啥的。
 

一、构造函数实现(借用构造函数):

function Super(arg){ this.arr1 = "I'm super "+arg; this.show = function(){ alert(this.arr1); } } Super.prototype.say = function(){ alert(this.arr1); } function suber(arg){ Super.apply(this, arguments); //在suber的上下文中运行super } var sub =new suber("suber"); var sub2 = new suber("suber1"); console.log(sub.arr1); //I'm super suber console.log(sub.show); //function (){ alert(this.arr1);} console.log(sub.say); //undefined console.log(sub.show === sub2.show); //false

哎呀,发现sub.say是undefined,这说明它没有被继承过来啊,下边两个对象sub,sub2的show不相等,说明两个函数指向了两个不同的对象,也就是说被复制了两份出来。

所以这个方法实现继承的话原型对象上的属性和方法没有被继承过来,Super上的属性和方法为每个new出来的对象分别复制一份。
所以单单使用这个方法来实现继承还是不妥啊,因为原型上的方法都没有被继承过来啊。于是大神们就想到了原型继承

二、原型继承:

function Super(arg){ this.arr1 = "I'm super "+arg; this.show = function(){ alert(this.arr1); } } Super.prototype.say = function(){ alert(this.arr1); } function suber(arg){} suber.prototype = new Super(); var sub = new suber("suber1"); var sub2 = new suber("suber2"); console.log(sub.arr1); //I'm super undefined console.log(sub.show); //function (){ alert(this.arr1);} console.log(sub.say); //function (){ alert(this.arr1);} console.log(sub.show === sub2.show); //true; console.log(sub.say === sub2.say); //true;


这次是arr1继承过来了,但是参数没有添加进来,是undefined,所以这个方法子类声明时候这个参数传进来付类继承过来的这个属性没法收到。其他的都还算正常。show和say都继承过来了。但是有一点儿需要注意,say是通过super的原型对象继承过来的,而show是新建super对象实例时实例的属性。

那么怎么实现参数传输又能把原型里边的东东继承过来呢,当然上边两种方法组合一下就好了啊,于是前辈们又发明了下面这种方法


三、组合继承(借用构造函数并设置原型):

function Super(arg){ this.arr1 = "I'm super "+arg; this.show = function(){ alert(this.arr1); } } Super.prototype.say = function(){ alert(this.arr1); } function suber(arg){ Super.apply(this, arguments); } suber.prototype = new Super(); var sub = new suber("suber1"); var sub2 = new suber("suber2"); console.log(sub.arr1); //I'm super suber1 console.log(sub.show); //function (){ alert(this.arr1);} console.log(sub.say); //function (){ alert(this.arr1);} console.log(sub.show === sub2.show); //false; console.log(sub.say === sub2.say); //true;

这次几乎完美了,但是可以发现sub.show 和sub2.show并不相等啊,这是为啥呢,因为apply这个地方使得show成为了suber的自己的属性,那么就吧suber原型里的show(Super的当做suber原型对象的实例对象的show)给覆盖了,所以又变成每次复制一个,当然这个地方没有办法避免啊。为了不产生这种多余的开消这种可以共用的函数可以多放到原型对象里边。
因为suber的构造里边的调用,和给suber原型对象赋值时候的调用导致Super被调用了两次,那么每次new suber对象时候就调用了两次Super,调用两次就会产生两个实例对象,需要消耗多余的资源了。

于是前辈们为了解决这个问题又开了开脑洞,开发出来下面这种方法。
 

四、寄生组合继承:
该方法跟方法三最主要的不同就是把父类原型赋给了子类原型而不是父类示例,看例子

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgyjxf.html