JavaScript学习笔记(三)—— 继承的实现

一、JavaScript实现继承的三层含义:
 
    ① 子类的实例可以共享父类的方法;
    ② 子类可以覆盖父类的方法或者扩展新的方法;
    ③ 子类和父类都是子类的实例。
 

二、实现继承的几种方法:
 
1、原型链继承
例:function Box( ){           //被继承的函数叫超(父)类型;
     this.name = “Andy”;
     }
       function Desk( ){           //继承的函数叫子类型
     this.age = 25; 
     }
Desk.prototype = new Box( );   //将Box构造里的信息和原型信息都给Desk
var desk = new Desk( );
alert(desk.name);    //返回 Andy;
 
Desk.prototype.name = "Jack”; //重写父类型中的属性
var desk2 = new Desk();          
alert(desk2.name);   //返回 Jack;
 
ps:  ① 如上例,若子类型要重写父类型中某个方法或属性,给原型添加的代码一定要放在替换原型的语句之后。
       ② 在通过原型链继承时,不能使用字面量创建原型对象,这样会重写原型链
     

2、对象冒充法(即 借用构造函数)
     解决引用共享和超类型无法传参的问题,采用“对象冒充”技术继承。
例:function Box (name,age){
       this.name  = “Andy”;
       this.age = 25;
       this.family = [“姐姐”,“哥哥”,“妹妹”];
       }
 
      function Desk(name,age){
        Box.call(this,name,age)        //Desk 冒充 Box  
      }
   
      var desk = new Desk( );
      alert(desk.name);    //返回 Andy
      alert(desk.family);   //返回 姐姐,哥哥,妹妹
 
PS
       ① call和apply方法都可以实现继承,两者的第二个参数不同,call是一个个的参数列表 
       apply的第二个参数是数组。
       例:Box.call(this,name,age);
             Box.apply(this,[name,age]);
 
       ② 对象冒充只能继承构造里的信息
       例:接上例添加:
            Box.prototype.family = “家庭”;
            alert(desk.family);    //继承不了,仍然返回 姐姐、哥哥、妹妹
       

3、组合继承法(对象冒充+原型链继承方式)
使用原型链实现对原型属性和方法的继承,使用对象冒充来实现对实例属性的继承
例:function Box(name,age){
        this.name = name;
        this.age = age;  
}  
Box.prototype.work = function(){
       return  this.name+this.age+”运行中”;
   }
      function Desk(name,age){
       Box.call(this,name,age)             //对象冒充,构造里继承
     }
Desk.prototype = new Box( );        //原型链继承
var desk = new Desk(“Andy”,25);
alert(desk.work( ) );   //返回 Andy 25 运行中
 

小 结   
原型链继承中,包含引用类型值的原型的原型属性会被所有实例共享,并且创建子类型的实例时,不能向超类型的构造函数中传递参数,因此实践中很少会单独使用原型链。
 
对象冒充(借用构造函数法)中,可以通过call()或者apply()方法在新创建的对象上实行构造函数,解决了原型链继承法中共享和无法传参的问题。但对象冒充只能继承构造里的信息,会导致函数无法复用
 
组合继承中,使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承,既能实现函数复用,又能保证每个实例都有自己的属性,因此,成为JavaScript常用的继承模式(唯一问题:超类型会被调用两次

4、原型式继承(与原型链继承类似)
例://临时中转函数
        function obj(o){      //o表示将要传递进入的一个对象
        function  F( ){ }           // 临时新建的对象,用来储存传递过来的对象
        F.prototype = o;        //将o对象实例赋值给F构造的原型对象
        return new F( );
       }
     var box = {                     //相当于 var box = new Box();
           name:”Andy”,
           age:28,
           family:[“哥哥”,“姐姐”]
           }
 
      var box1 = obj (box);    //相当于 var box1 = new F();
      alert(box1.name);         //   返回 Andy
      alert(box1.family);        //    返回 哥哥,姐姐
      box1.family.push(“弟弟”);
      alert(box1.family);        //    返回 哥哥,姐姐,弟弟
      var box2 = obj(box);
      alert(box2.family);        // 返回 哥哥,姐姐,弟弟
 
因此,原型式继承的问题是包含引用类型值的属性始终会共享相应的值
 

5、寄生式继承(原型式 + 工厂模式)
例:function obj(o){                  //与原型式继承相同,临时中转函数
       function F(){}    
       F.prototype = o;  
       return new F();
        }
 
      function create(o){        //  创建寄生函数
       var f = obj(o);
        f.run = function(){
        return this.name+"方法";
        }
        return f ;
        }
 
var box = {          
    name:”Andy",
    age:100,
    family:["哥哥","姐姐"]
  };
 
var box1 = create(box);
alert(box1.name)  //Andy
alert(box1.run());  //Andy 方法
 
此方法可以为对象添加函数,但也不能做到函数复用,与借用构造函数继承类似。
 

6、寄生组合式继承
function obj(o){
function F( ){};
F.prototype = o;
return new F( ); 
 }
 
function create(box,desk){
 var f = obj(box.prototype);
 f.constructor = desk;    //调整原型构造指针
desk.prototype = f; 
}
 
function Box(name,age){
this.name = name;
this.age = age;
}
Box.prototype.run=function(){
   return  this.name+this.age +”启动”; 
}
function Desk(name,age){
  Box.call(this,name,age);    //对象冒充
}
create(Box,Desk);           //替代 Desk.prototype = new Box( );
var desk = new Desk(“Andy”,28);
alert(desk,run());    //弹出  Andy28启动
 
 
问题:组合继承是 js 最常用的继承方式,但组合继承的超类型会在使用过程中会被调用两次,一次在创建子类型时,一次在子类型构造函数内部,即:Desk.prototype =  new Box( );第一次调用;Box.call(this,name,age);第二次调用。
解决寄生组合式继承则解决了超类型被调用两次的问题
原文地址:https://www.cnblogs.com/emory/p/5034463.html