prototype的六种继承方式以及优缺点

1.1 原型的六种继承序言

之前学习了与原型链相关的prototype的概念,现在来继续深入学习以下六种继承的原理以及它们的优缺点。

2.1 原型链继承

2.1.1 原型链继承的基本思路

通过原型链普通的继承原理,一个原型对象继承多个对象的属性以及方法。

2.1.2 原型链继承的代码实现

      function user(name){
    
    
        this.name = 'a'
        this.age = 10
        this.thing = []
      }

      function vip(){
    
    

      }

      vip.prototype = new user()
      // 为什么需要以 子类构造函数的显式原型 = 实例化的父类构造函数 书写呢?
      // 原型链的层次关系 vip.prototype → new user()实例化对象 → [ new user()实例化对象.__proto__ 既是 user.prototype ]

      vip.prototype.constructor = vip

      user.prototype.addThing = function(item){
    
    
        this.thing.push(item)
      }

      let warn = new vip('xxx')
      console.log(warn)

      let a = new vip()
      a.addThing('hasVipCard')
      console.log('a',a)

      let b = new vip()
      console.log('b',b)

运行截图
在这里插入图片描述

2.1.3 原型链继承的优缺点

  • 优点1:代码实现比较简单。
  • 缺点1:不能够通过子类构造函数的同时传参给父类构造函数。
  • 缺点2:修改其中一个子类实例对象,会影响其它子类实例对象引用值。

2.2 盗用构造函数

2.2.1 盗用构造函数的基本思路

在子类构造函数中重新调用父类构造函数进行传参。

2.2.2 盗用构造函数的代码实现

 function user(name,thing){
    
    
        this.name = name
        this.thing = thing
      }

      function vip(name,thing){
    
    
        user.call(this,name,thing)
        //call意思是使用this对象指向到user对象,vip可以直接调用user所有属性以及方法
      }

      vip.prototype.addThing = function (item){
    
    
        this.thing = [item]
      }

      let a = new vip('小明',['书包'])
      console.log('a',a)
      let c = new vip()
      c.addThing('笔')
      console.log('c',c)
      let d = new vip()
      console.log('d',d)

运行截图
在这里插入图片描述

2.2.3 盗用构造函数的优缺点

  • 优点:能够通过子类构造函数的同时传参给父类构造函数。
  • 缺点:子类不能够获取到父类的原型对象定义的方法,因为没有继承(即没有原型链)。

2.3 组合继承(盗用构造函数+原型链继承)

2.3.1 组合继承的基本思路

通过盗用构造函数来,从子函数中定义的this对象指向父类并给给它传值。又可以通过原型链继承,子类生成的实例可以调用父类定义的方法。

2.3.2 组合继承的相关代码

function user(name,thing){
    
    
        this.name = name
        if(thing){
    
    
          this.thing = [thing]
        }else{
    
    
          this.thing = thing
        }
      }

      function vip(name,thing){
    
    
        user.call(this,name,thing)
      }

      user.prototype.addThing = function(item){
    
    
        if(this.thing){
    
    
          this.thing.push(item)
        }else{
    
    
          this.thing = [item]
        }
      }

      vip.prototype = new user()

      let a = new vip('小红','笔')
      console.log('a',a)

      let b = new vip()
      b.addThing('书包')
      console.log('b',b)

      let c = new vip()
      console.log('c',c)

运行截图
在这里插入图片描述

2.3.3 组合继承的优缺点

  • 优点:每个子类生成的实例互相独立不影响,且可以传值给父类或使用父类的方法。
  • 缺点:父类构造函数中的局部变量保持相同(未定义的也会占用父类构造函数的空间)

2.4 原型式继承

2.4.1 原型式继承的基本思路

所生成的原型指向第一个参数(原型对象),第二个参数是定义生成新的原型对象中的各个变量属性规则。

2.4.2 原型式继承的相关代码

// Object.create()底层实现原理 (ES6的新特性)
      Object.create =  function (o) {
    
    
        var F = function () {
    
    };
        F.prototype = o;
        return new F();
      };

      let person = {
    
    
        name:'萨哈',
        things:[]
      }


      let a = Object.create(person)
      a.name = '小红'
      a.things.push('笔')
      console.log('a',a)

      let b = Object.create(person,{
    
    
        //定义该原型对象里每个变量的属性
        name:{
    
    
          //该变量的值
          value:'小黄',
          //该变量是否可写
          writable: false,
          //该变量是否能遍历
          enumerable:true,
          //是否允许外部操作修改这个变量的其它属性值(value属性除外)
          configurable:true
        }
      })
      b.name = '我要修改'
      console.log('b',b)

运行截图
在这里插入图片描述

2.4.3 原型式继承的优缺点

  • 优点:不需要定义子类构造函数,已经原型链.prototype的指向操作。
  • 缺点:父类构造函数的变量值还是会共享,保持原型链继承的特征。

2.5 寄生式继承

2.5.1 寄生式继承的基本思路

是类似于原型寄生构造函数与工厂模式,在封装继承过程中增强需要使用到的构造函数方法。

2.5.2 寄生式继承的相关代码

// 内部定义object()传入的参数,它作为父类原型对象,并与其return返回的新实例F构建继承关系
      function object(o){
    
    
        function F(){
    
    }
        F.prototype = o
        return new F()
      }

      function smallObject(item){
    
    
        //给原来item的原型对象与新建的clone原型对象添加继承关系
        let clone = object(item)
        //给新的clone原型对象添加新的内部构造方法(增强)
        clone.outPut = function(){
    
    
          console.log('我是由item原型对象生成的打印')
        }
        return clone
      }

      let person = {
    
    
        name:'a'
      }

      //生成一个新的原型对象a,并拥有了person的属性以及方法。(其中包括方法outPut())
      let a = smallObject(person)
      a.outPut()
      console.log('a',a)

      let b = smallObject()
      console.log('b',b)

运行截图
在这里插入图片描述

2.5.3 寄生式继承的优缺点

  • 优点:需要新建的继承新建原型对象不是构造函数或者自定义对象,可以适用寄生式继承。
  • 缺点:原型需要运用到的构造函数是在于生成新原型对象内部定义的,复用的话会有局限性,无法针对多场景继承原型对象。

2.6 寄生式组合继承

2.6.1 寄生式组合继承的基本思路

组合继承因为会调用两次父类构造函数(再子类构造函数指向父类而调用+添加继承关系而调用),会存在效率上的问题,以及父类的属性生成时也会有默认属性。而提出寄生式组合继承,原理是封装子类与父类的关系以及继承关系,只调用一次父类构造函数,以节省空间,防止产生多余。

2.6.2 寄生式组合继承的相关代码

//定义继承关系(子类与父类的关系以及继承关系的封装)
      function object(o){
    
    
        function F(){
    
    }
        F.prototype = o
        return new F()
      }

      function bindprototype(parent,son){
    
    
        let p = object(parent.prototype)
        p.constructor = son
        son.prototype = p
      }

      //父类构造函数
      function personN(name){
    
    
        this.name = name
        this.thing = ['书包']
      }

      //子类构造函数
      function personV(name,age) {
    
    
        //父类对象指向this的对象,(若使用下行的personV.prototype = new personN()), personN.call()第二次调用就是第二次调用personN()
        personN.call(this,name)
        this.age = age
      }

      // 下第一次调用personN()
      // personV.prototype = new personN()


      bindprototype(personN,personV)

      var a = new personV('小红','18')
      a.thing.push('笔')
      console.log('a',a)

      var b = new personV('小黄')
      console.log('b',b)

运行截图
在这里插入图片描述

2.6.3 寄生式组合继承的优缺点

  • 优点:设置继承关系时只调用一次,节省空间。
  • 缺点:封装继承关系时,代码比较繁琐难以理解。

3.1 结论

从这六种继承方法种,寄生式组合继承是现在开发人员普遍使用的继承方法,既满足性能以及代码空间的利用率,又可以对原型与原型之间的继承方法的封装,实现原型链的继承效果。经过本人的几天学习,6种继承方法比较繁琐,有些部分也比较难懂需要更深入地加重复习一下。

猜你喜欢

转载自blog.csdn.net/Ak47a7/article/details/129902785