继承

前言

JavaScript是单继承的,只能继承一个父类

原型链继承

  1. 定义: 子类构造函数的prototype指向父类创建出来的对象
function Person(name,age){
  this.name = name
  this.age = age
  this.run = function(){ return 'run' }
}
function Student( stNo ){
  this.stNo = stNo
}
Student.prototype = new Person('nano',18)
const stu1 = new Student(11)
let run = stu1.run()
console.log(run) --> run
  1. 缺点
  2. 继承的属性无法打印出来,因为是原型属性[[prototype]], 不可枚举
  3. 如果父类里的值是引用类型,某个子类在修改该引用类型的值时其它子类也会被影响
  4. 声明子类的时候无法传递参数直接赋值给继承父类的属性,必须得在父类声明时传递

借助构造函数继承

  1. 在原型链继承的基础上借用构造函数继承
function Person(name,age){
  this.age = age
  this.name = name
  this.say = function(){ console.log(this.name) }
}

function Student(name,age,stNo){
  /* 
    借助构造函数,指定this的绑定,这样实现子类传递参数,父类统一接收
    在子类的函数this中添加了属性,这样就可以打印出来
    每次调用子类,都会独有this,这样解决了引用类型指针共享的问题
  */
  Person.call(this,name,age)
  this.stNo = stNo
  this.run = function(){ console.log( 'run') }
}
Student.prototype = new Person()
const stu = new Student('nano', 19, '05')
console.log( stu )
  1. 优点: 解决了原型链继承的全部问题
  2. 缺点:
    (1) 父类函数至少被调用了两次
    (2) 子类的原型对象[[prototype]]上会多一些没有必要存在的属性

寄生式继承(针对于对象)

  1. 定义: 原型继承结合工厂模式
    // 父类
    let personObj = {
      running(){
        console.log('run')
      }
    }
    
    // 工厂函数
    function createStu(personObj,name){
      // 创建一个原型指向父类的子类
      let obj = Object.create(personObj)
      obj.name = name
      obj.say = function(){}
      return obj
    }
    let stu1  = createStu(personObj, 'nano')
    let stu2  = createStu(personObj, 'roily')
  2. 缺点:
    (1) 重复创建函数
    (2) 无法知道是什么类型的类

寄生组合式继承(最终方案)

  1. 定义: 将借助构造函数继承和寄生式继承结合
    // 为了方法复用,往往会封装一个工具函数
    function inherit(parent,child){
      child.prototype = Object.create(parent.prototype)
      Object.defineProperty(child.prototype,'constructor',{
        value:child,
        configure:true,
        enumerable:false,
        writable:true   
      })
    }
    function Parent(name,age){
      this.name = name
      this.age = age
      this.say = function(){}
    }
    fcuntion Child(name,age,sno){
      Parent.call(this,name,age)
      this.sno = sno
      this.study = function (){}
    }
    /* 没有进行函数复用封装
      // 寄生式继承,Object.create用来创建一个原型[[prototype]]指向父类的空对象
      Child.prototype = Object.create(Parent.prototype) --> createObj(Parent.prototype) [createObj函数在下方]
      // 将寄生式继承的名称由父类改为子类,实际就是更改显示原型上constructor函数的指向
         Object.defineProperty(Child.prototype,'constructor',{
          value:Child, //  constructor 指向构造函数
          configure:true,
          enumerable:false,
          writable:true   
      })
    */
    
    // 进行函数复用封装
    inherit(Parent,Child)
    Child.prototype.eating = function( ){}
    
    
    // 如果不适用Object.create()函数该如何实现寄生式继承?
      function createObj(o){
          // 声明一个新函数
        function Foo(){}
          // 将该函数的显示原型指向传进来的对象
        foo.prototype = o
          // 返回new出来的对象,该对象的原型指向Foo的prototype,指向o
        reutrn  new Foo()
      }

    注意: ES6的class继承就是该模式的语法糖


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!