• javascript 继承


    继承通过原型链来实现,我们知道,构造函数的prototype属性指向构造函数原型对象,如果为该对象添加成员,就能够实现实例之间的共享,那么,如果将原型指向另一个对象,就可以拥有该对象的所有成员。

    这就是所谓的继承,继承链并非单一,理论上来讲,能够无限的继承下去, 也就是我们常说的原型链

    即:

         a.prototype= new b(); 

         b.prototype=new c();

      这样,a不仅拥有了b的所有成员,包括实例成员,还包含了c的所有成员。

    之所以能够实现继承,是因为如果在调用对象成员时,如果找不到该成员,就会在原型中继续找,而原型又是另一个对象的实例,那么会继续找下去,直到找到为止,如果没有找到,就报错。

    1.原型继承: 

    复制代码
    function superCon(name,age){
          this.name=name;
          this.age=age;
        }  
        superCon.prototype.showinfo=function() 
        {
           alert(this.name+this.age);
        } 
        function sub(){} 
        sub.prototype= new superCon('moersing',18); 
        var sub1 = new sub(); 
        sub1.showinfo(); //moersing18
    复制代码

    上述代码实现了从 sub继承superCon的过程,咋一看没什么问题,但是,但父类在拥有引用成员的时候,就会出问题了。

    复制代码
    function superCon(name,age){ 
    
          this.name=name; 
    
          this.age=age; 
    
          this.books=['c#','vb.net','javascript']; 
    
        }   
    
         superCon.prototype.showbooks=function(){ 
    
         this.books.forEach(function(c,i,a){document.write(c);})} 
         function sub(){}  
    
        sub.prototype= new superCon();  
    
        var sub1 = new sub('moersing',18);  
    
        sub1.books.push('css3'); 
    
        sub1.showbooks(); //分别输出,'c#','vb.net','javascript','css3'; 
    
        var sub2 = new sub(); 
    
        sub2.showbooks(); //分别输出,'c#','vb.net','javascript','css3';
    复制代码

    我们希望sub2拥有单独的一个books数组,但是事实却不是这样的,原因还是 原型共享 问题,由于sub.prototype指向了superCon的实例,也就是说,相当于在sub.prototype中定义了一个books数组,根据原型共享的概念,通过实例改变原型中的引用类型,那么,在其他的实例中也会反映出来,为了解决这个问题,可以使用另一种继承方式,那就是 组合继承

    2.组合继承

    复制代码
    function superCon(name,age){ 
    
          this.name=name; 
    
          this.age=age; 
    
          this.books=['c#','vb.net','javascript']; 
    
        }   
    
         superCon.prototype.showbooks=function(){ 
         this.books.forEach(function(c,i,a){document.write(c);})} 
     
    function sub(){  
             superCon.call(this,['moersing',18]); 
        }  
    
        sub.prototype= new superCon();  
    
        var sub1 = new sub();  
    
        sub1.books.push('css3'); 
    
        sub1.showbooks(); //分别输出,'c#','vb.net','javascript','css3'; 
    
        var sub2 = new sub(); 
    
        sub2.showbooks(); //分别输出,'c#','vb.net','javascript';
    复制代码

    代码差不多,但是解决了共享的问题,在sub函数中调用了superCon的call方法,将this传进去,并给了两个参数。

    有人可能会迷糊,但是原理很简单,利用call来改变父类构造函数的运行环境(应该说活动对象上下文),而这个环境就是this。那么,this是谁? 

    就是new sub();也就是sub对象的实例,说简单点就是,sub.prototype是指向了superCon的实例没错,但是,在call的时候,sub借用了super重新创建了实例成员(name,age,books),也就覆盖了prototype 中的name,age和books。

    那么一切就顺理成章了:  

       1.sub.prototype指向superCon的实例,继承了superCon所有成员。

       2.在new sub()的时候,借用了父类的构造函数(call调用),那么,子类的实例就拥有了父类所有实例成员,自然也就拥有了单独的books数组了。

    这样就能解决继承中,原型共享的问题。

    但是!!!不知各位发现没有??sub.prototype = new superCon(); 也就是说,sub的原型还是指向了superCon的实例,那么,sub原型就拥有:name,age,books和一个showbooks方法。

    只不过在call的时候被覆盖掉了,通过一个实例可以验证出来: 

    delete sub1.books; 
    sub.showbooks(); //分别输出,'c#','vb.net','javascript',少了一个'css3'。

    我们通过使用delete 删除实例属性books,但是调用showbooks的使用,还是能够打印数组的内容,但是却是没有push之前的内容。

    可以看出,实际上,在sub实例中拥有了name,age,books,而原型也有name,age,books,只是在访问的时候,会先访问实例中的而不是原型中的,所以,借用构造函数也并非没有缺点,需要创建两次实例成员。

    这是目前业界用的最多的继承方式,如果你觉得别扭,可以尝试使用 道格拉斯▪克罗克福德提出的  "寄生组合式继承".

    关于 道格拉斯▪克罗克福德 提出的原型式继承,本人不是很赞同,一来需要手动创建一个对象,二来,其原理就是拷贝该对象给另一个对象的原型,也就存在原型对象引用类型共享的问题,也存在性能的问题(拷贝对象),另外,这种方式很麻烦。

    对于道格拉斯▪克罗克福德提出的两种继承方式,参见 《javascript 高级程序设计 第三版》

  • 相关阅读:
    A1117. Eddington Number
    A1116. Come on! Let's C
    A1115. Counting Nodes in a BST
    A1114. Family Property
    A1113. Integer Set Partition
    OC之【NSValue的使用】
    OC之【NSDate使用】
    NSString与int和float的相互转换
    OC之【@protocol协议】
    OC之【深拷贝(mutableCopy)和浅拷贝(copy)】
  • 原文地址:https://www.cnblogs.com/bydzhangxiaowei/p/8030602.html
Copyright © 2020-2023  润新知