JS继承的几种方式及优缺点

一.原型链继承:

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

      function Father(){
        this.age=10
        this.phone={
          first:"华为",
          second:"小米"
        }
      }
      Father.prototype.getage=function(){
        return this.age
      }
      function Son(name,money){
        this.name=name
        this.money=money
      }
      Son.prototype=new Father() //子类型的原型为父类型的一个实例对象
      Son.prototype.constructor=Son //让子类型的原型的constructor指向子类型
      Son.prototype.getmoney=function(){
        return this.money
      }
      var son=new Son("小米",1000)//
      var son2=new Son()
      console.log(son.age)//10
      console.log(son.getage())//10
      console.log(son.name)//小米
      console.log(son.getmoney())//1000
      console.log(son instanceof Son)//true
      console.log(son instanceof Father)//true
      son.phone.first="魅族"//更改一个子类的引用属性,其他子类也会受影响
      console.log(son2.phone.first)//魅族

           优点:1.通过子类实例可以直接访问父类原型链上和实例上的成员

                      2.  相对简单

           缺点:1.创建子类实例时,无法向父类构造函数传参

                      2.父类的所有引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响

二.构造函数继承:

       原理:在子类构造函数中调用父类构造函数,可以在子类构造函数中使用call()apply()方  法改变this指向

      function Father(name,age){
        this.name=name
        this.age={age:age}
      }
      Father.prototype.getname=function(){
        return this.name
      }
      function Son(name,age,money){
        Father.call(this,name,age)//修改Father的this
        this.money=money
      }
      Son.prototype.getmoney=function(){
        return this.money
      }
      var son=new Son("小明",12,1000)
      var son2=new Son("小李",11,999)
      console.log(son.name)//小明
      console.log(son.getname())//报错 无法继承父类原型上的属性与方法
      console.log(son.money)//1000
      console.log(son.getmoney())//1000
      console.log(son instanceof Father)//false
      console.log(son instanceof Son)//true
      console.log(son.age.age)//12
      console.log(son2.age.age)//11 父类的引用属性不会被共享

        优点:1.可以在子类实例中直接向父类构造函数传参

                   2.父类的引用属性不会被子类共享

        缺点:1.无法继承父类原型上的属性与方法

三.组合继承:

       原理:组合上述两种方法就是组合继承。用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。

      function Father(name,age){
        this.name=name
        this.age={age:age}
      }
      Father.prototype.getname=function(){
          return  this.name
      }
      function Son(name,age,money){
        Father.call(this,name,age)//能够看到父类型属性
        this.money=money
      }
      Son.prototype=new Father()//能看到父元素方法
      Son.prototype.constructor=Son//让子类型的原型的constructor指向子类型
      Son.prototype.getmoney=function(){
        return this.money
      }
      var son=new Son("小明",12,1000)
      var son2=new Son("小李",18,1999)
      console.log(son.name)//小明
      console.log(son.getname())//小明
      console.log(son.money)//1000
      console.log(son.getmoney())//1000
      console.log(son instanceof Father)//true
      console.log(son instanceof Son)//true
      console.log(son.age.age)//12
      console.log(son2.age.age)//18 父类构造函数中的引用属性不会被共享

         优点: 1.可以在子类实例中直接向父类构造函数传参
                  2. 通过子类实例可以直接访问父类原型链和实例的成员

                  3.父类构造函数中的引用属性不会被子类共享

         缺点:调用了两次supertype构造函数,一次在赋值Son的原型时,一次在实例化子类时call                                          调用,这次调用会屏蔽原型中的两个同名属性。

四:原型式继承:

           原理:利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。

      function object(obj){
        function F(){}
        F.prototype=obj//对传入其中的对象执行了一次浅复制,将构造函数F的原型直接指向传入的对象。
        return new F()
      }
      var person={
        name:"小李",
        friends:["小米","小兰"],
        sayname:function(){
          console.log(this.name)
        }
      }
      var person1=object(person)
      person1.name="小王"
       person1.friends.push("小黑")
      console.log(person1.friends)//['小米', '小兰', '小黑']
      person1.sayname()//小王
      var person2=object(person)
      person2.name="小鱼"
      person2.friends.unshift("小葵")
      console.log(person2.friends)// ['小葵', '小米', '小兰', '小黑']
      person2.sayname()//小鱼
      console.log(person.friends)//['小葵', '小米', '小兰', '小黑']

            缺点: 1.子类实例不能向父类传参

                        2.父类的所有引用属性会被所有子类共享

五:寄生式继承

           原理: 在原型式继承的基础上,增强对象,返回构造函数

      function object(obj){
        function F(){}
        F.prototype=obj
        return new F()
      }
      function createAnother(obj){
        var clone=object(obj)
        clone.getname=function(){    //增强对象
          console.log(this.name)
        }
        return clone
      }
      var person={
        name:"小李",
        friends:["小米","小兰"],
      }
      var person1=createAnother(person)
      person1.friends.push("小黑")
      person1.name="小红"
      console.log(person1.friends)// ['小米', '小兰', '小黑']
      person1.getname()//小红
      var person2=createAnother(person)
      console.log(person2.friends)//['小米', '小兰', '小黑']
      person2.getname()//小李

             缺点: 1.子类实例不能向父类传参

                         2.父类的所有引用属性会被所有子类共享

                          (同原型式继承)

六:寄生式组合继承:

           原理:结合借用构造函数传递参数和寄生模式实现继承

    function object(obj){
      function F(){}
      F.prototype=obj
      return new F()
    }
    function GetPrototype(Father,Son){
      var prototype=object(Father.prototype)  // 创建对象,创建父类原型的一个副本
      prototype.constructor=Son  // 增强对象,弥补因重写原型而失去的默认的constructor 属性
      Son.prototype=prototype  // 指定对象,将新创建的对象赋值给子类的原型
    }
    function Father(name){
      this.name=name
      this.color=["blue","pink","black"]
    }
    Father.prototype.getname=function(){
      return this.name
    }
    function Son(name,age){
      Father.call(this,name)
      this.age=age
    }
    GetPrototype(Father,Son) 这一句,替代了组合继承中的Son.prototype = new Father() 
    Son.prototype.getage=function(){
      return this.age
    }
    var son1=new Son("小米",18)
     var son2=new Son()
    son1.color.push("green")
    console.log(son1.getname())  //小米
    console.log(son1.name)  //小米
    console.log(son1.color)  //['blue', 'pink', 'black', 'green']
    console.log(son2.color)  // ['blue', 'pink', 'black']
    console.log(son1 instanceof Father)//true

            优点:1. 只调用一次父类构造函数

                       2. 子类可以向父类传参

                       3. 父类方法可以复用

                       4. 父类的引用属性不会被共享

                       这是最成熟的方法

七:拷贝继承:

       原理:通过遍历复制前一个对象的属性和方法达到拷贝的效果

    function Father(){
      this.name="小明",
      this.phone={
        first:"小米",
        second:"华为"
      }
    }
    Father.prototype.getname=function(){
      return this.name
    }
    function Son(name){
      var father=new Father()
      for(var i in father){
        Son.prototype[i]=father[i]
      }
    }
    var son=new Son()
    var son2=new Son()
    son.phone.first="小李"
    console.log(son.name)//小明
    console.log(son.getname())//小明
    console.log(son2.phone.first)//小李
    console.log(son instanceof Father)//false

            缺点:1.效率极低,内存占用高(因为要拷贝父类的属性)

                       2.无法获取父类不可枚举的方法(for in不能访问到的)

                       3.父类的所有引用属性会被所有子类共享

八:ES6 class继承

      class Father {
        constructor(name,age){
            this.name=name
            this.age=age
        }
        call(){
            return "打电话"
        }
      }
      //子类继承父类——语法:class 子类 extends 父类
      class Son extends Father{
        constructor(name,age,height){
            //super在子类的构造方法中调用父类的构造方法
            super(name,age)     //this操作必须放在super后面
            this.height=height
        }
        play(){
            console.log("玩游戏")
        }
      }
      var son= new Son("小王",16,180)
      console.log(son.name)//小王
      console.log(son.call())//打电话
      console.log(son.height)//180

         优点:原理还是参照寄生组合继承,基本原理是一样,语法糖,写起来方便,比较完美

你可能感兴趣的:(JavaScript,javascript,前端)