ES5构造函数的继承

221 阅读1分钟

今天我们来说一下构造函数的继承都有哪些方式以及优缺点是什么?

第一种:原型链继承

 <script>
  //先声明父类的构造函数
  function Father(name) {
    this.name = name
    this.list = ['a']
  }
  Father.prototype.sayHi = function () {
    console.log('hello')
  }
  //声明子类的构造函数
  function Son() {
  }
  Son.prototype = new Father()
  //创建两个实例对象
  let xiaoming = new Son('小明')
  let xiaobai = new Son('小白')
  xiaoming.sayHi() //方法也可以正常使用
  console.log(xiaoming); // 输出{} 并没有输出{name:'小明'}
  console.log(xiaobai); // 输出{} 并没有输出{name:'小白'}
  //写到这里你会发现缺点1.无法向父类构造函数传参
  //然后我们往list添加一个字符串b
  xiaoming.list.push('b')
  console.log(xiaoming.list) //输出[a,b]
  console.log(xiaobai.list) //输出[a,b]
  /*
   这时候我们发现所有实例都被影响到了
   这是因为执行xiaoming.list的时候,由于xiaoming实例身上并没有list这个属性,所以往xiaoming实例的原型上找,由于是原
   型链继承,所以Son的原型继承了Fahter构造函数new出来的实例的属性和方法,所以修改的也是原型上的属性list ,由于
   xiaoming和xiaobai指向的是同一个原型,所以导致被修改,
   这时候我们发现第缺点2.若父类构造函数共有属性为引用类型,原型上属性的改变会作用到所有的实例上
  */
</script>

第二种:借用构造函数继承

 <script>
  //先声明父类的构造函数
  function Father(name) {
    this.name = name
    this.list = ['a']
  }
  Father.prototype.sayHi = function () {
    console.log('hello')
  }
  //声明子类的构造函数
  function Son(name) {
    //使用call方法,call方法可以改变this的指向并且立即执行该函数
    Father.call(this, name)
  }

  //创建两个实例对象
  let xiaoming = new Son('小明')
  let xiaobai = new Son('小白')
  xiaoming.sayHi() //但是我们又发现了一个新缺点:1.父类构造函数原型上的方法已经没办法使用了,请自行注释
  console.log(xiaoming); // 输出{name:'小明'} 
  console.log(xiaobai); // 输出{name:'小白'} 
  //写到这里你会发现上面原型链继承的缺点1.无法向父类构造函数传参已经可以解决了
  //然后我们往list添加一个字符串b
  xiaoming.list.push('b')
  console.log(xiaoming.list) //输出[a,b]
  console.log(xiaobai.list) //输出[a]
  //发现属性没有私有化也可以解决了
</script>

第三种:原型链+构造函数继承

//先声明父类的构造函数
  function Father(name) {
    //加了这句代码后我们发现被输出了3次,分别是new Father的时候1次,创建两个实例的时候输出了2次,所以我们还可以进行
    优化,我们来看方法4
    console.log('father输出了')
    this.name = name
    this.list = ['a']
  }
  Father.prototype.sayHi = function () {
    console.log('hello')
  }
  //声明子类的构造函数
  function Son(name) {
    //使用call方法,call方法可以改变this的指向并且立即执行该函数
    Father.call(this, name)
  }
  Son.prototype = new Father() //原型链继承
  //创建两个实例对象
  let xiaoming = new Son('小明')
  let xiaobai = new Son('小白')
  xiaoming.sayHi() //方法可以正常调用
  console.log(xiaoming); // 输出{name:'小明'} 
  console.log(xiaobai); // 输出{name:'小白'} 
  //可以向父类构造函数传参
  //然后我们往list添加一个字符串b
  xiaoming.list.push('b')
  console.log(xiaoming.list) //输出[a,b]
  console.log(xiaobai.list) //输出[a]
  //属性也实现了私有化

第四种:寄生继承

1.为了方便看,多余的代码我们就删除了

 //先声明父类的构造函数
  function Father(name) {
    console.log('father输出了')
    this.name = name
  }
  Father.prototype.sayHi = function () {
    console.log('hello')
  }
  function Son(name) {
    Father.call(this, name)
  }
  Son.prototype = Object.create(Father.prototype)//寄生继承
  //这时候就不会因为给Son的prototype继承的时候,多调用一次
  // Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的原型
  let xiaoming = new Son('小明')
  xiaoming.sayHi()
  console.log(xiaoming);