JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。JS面向对象知识中,继承是比较难比较抽象的一块内容,而且实现继承有很多种方法,每种方法又各有优缺点,需要对面向对象知识中的对象、原型、原型链、构造函数等基础知识掌握透彻,下面我们介绍四种继承的方法:原型链继承,构造继承,实例继承,组合继承。

既然要实现继承,那么首先我们得有一个父类,代码如下:

/*先定义一个父类对象*/
        function Animal(){   //动物类有什么特征和行为
         //属性
            this.name=null;
            this.AnimalColor=null;
            this.sex=null;
            this.age=null;
            this.type=null;
         //实例方法
            this.sleep=function (){
                return this.name+"会睡觉";
            }
        }
        // 原型方法  原型链追加(继承)  给原型方法里面追加属性和行为的
        Animal.prototype.eat=function (){
            return this.name+"会吃饭";
        }

原型链继承

核心: 将父类的实例作为子类的原型

1.定义一个子类对象

//定义一个子类对象
        function Cat(){
            this.soloMouse=function (){   //子类特有行为
                return this.name+"抓老鼠";
            }
        }

2.实例化子类对象

var a=new Animal();//实例对象
        Cat.prototype=a;
        Cat.prototype.speed=function (){
            return 5.0;
        }
        //实例化cat对象
        var c=new Cat();
        c.name="小花";
        c.type="猫科";
        c.AnimalColor="白黑";
        c.age="2";
        c.sex="母";
       /* c.prototype.name="小黑";*/
        console.log(c);
        console.log(c.sleep());
        console.log(c.soloMouse());
        console.log(c.eat());
        console.log(c.speed());
        console.log(c instanceof Cat);  //true
        console.log(c instanceof Animal);  //true

特点:

  1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  2. 父类新增原型方法/原型属性,子类都能访问到
  3. 简单,易于实现

缺点:

  1. 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
  2. 无法实现多继承
  3. 来自原型对象的所有属性被所有实例共享
  4. 创建子类实例时,无法向父类构造函数传参

构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

//定义一个父类
  function rou(name){
            this.name=name;
            this.eat=function (){
                return this.name+"吃肉";
            }
        }


//定义一个子类对象  同时实现继承
  function Cat(name,color,sex,age,type){
        
            Animal.call(this,name,color,sex,age,type);

           /* Animal.apply(this,[name,color,sex,age,type]);*/

            /*this.name=name;*/

            rou.apply(this,[name]);
   /* call apply  在args上有区别call 序列传参  apply  为数组传参*/



   var c=new Cat("小猫","白色","母",2,"猫");
        console.log(c);
        console.log(c.eat());
        console.log(c instanceof Cat); //true
        console.log(c instanceof  Animal); //false
        console.log(c instanceof  rou);  //false
        }

特点:

  1. 创建子类实例时,可以向父类传递参数
  2. 可以实现多继承(call多个父类对象)

缺点:

  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

实例继承

核心:为父类实例添加新特性,作为子类实例返回

//定义一个子类
        function Cat(){
            var a=new Animal();
            a.name="小猫";
            return a;   //不支持多继承
        }
 //实例化子类
        var c=new Cat();  //不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
        console.log(c);
        console.log(c.speed());
        console.log(c instanceof  Cat);  // false 实例是父类的实例,不是子类的实例
        console.log(c instanceof Animal);  //true

特点:

  1. 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

  1. 实例是父类的实例,不是子类的实例
  2. 不支持多继承

组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function rou(){
         this.eat=function (){
             return "吃肉";
         }
     }
     //定义一个子类对象
     function Cat(){
         Animal.call(this);  //构造继承
         rou.call(this);
     }
     Cat.prototype=new Animal();   //原型链继承

     var c=new Cat();
     console.log(c);
     console.log(c.speed());
     console.log(c.eat());

特点:

  1. 弥补了构造继承的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
  2. 既是子类的实例,也是父类的实例
  3. 不存在引用属性共享问题
  4. 可传参
  5. 函数可复用

缺点:

  1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)