面向对象从ES3到ES6的继承方法总结以及对比

继承是面向对象中一个非常重要的概念,javascript中可以实现继承,但不支持接口的继承,并且我们js中主要是依赖原型链实现继承的,下面说说从es3到es6中常见的几种继承方式:

首先我们假设我们有两个类一个Television(电视)Computer(电脑)

function  Television  () {
  this.name = '电视'
  this.telecontroller = '遥控器'
}

function  Computer  () {
  this.name = '电脑'
}

Television(电视)有个功能叫play(播放)Computer(电脑)有个compute(计算)的功能

function  Television () {
  this.name = '电视'
  this.telecontroller = '遥控器'
}
  
Television.prototype.play =  '播放'
  
function  Computer () {
  this.name = '电脑'
}
  
Computer.prototype.compute = '计算'

然而我们知道Computer(电脑)也有play(播放)这个功能功能。那你会想到再给Computer(电脑)也添加一个play(播放)的功能不就好了,但是这样无形中浪费内存的资源,而且写起来也不比较繁琐,这不是我们想要的。这时候 继承 就出场了,而且实现继承的方式有很多种各有优缺点,接下来我们将每一种拿出来并对比一下他们的优缺点:

一、原型链继承

先上代码:

function  Television  () {
  this.name =  '电视'
  this.telecontroller =  '遥控器'
}

Television.prototype.play =  '播放'

function  Computer  () {
  this.name =  '电脑'
}

// 必须将这句写到上面否则下面的方法会被覆盖掉
Computer.prototype =  new Television()

// 因为new Television()实例没有constructor方法 所以我们要将constructor指向Computer
Computer.prototype.constructor = Computer

Computer.prototype.compute =  '计算'

console.log(new Computer().name)  // 电脑
console.log(new Computer().compute)  // 计算
console.log(new Computer().play)  // 播放
console.log(new Computer().telecontroller) // 遥控器
console.log(Computer.prototype.name)  // 电视

[注意]

  • Computer.prototype = new Television()必须写在子类定义原型方法(如现在的compute方法)之前,否则子类定义的原型方法会丢失(因为Computer.prototype指向堆内存地址变了)
  • 因为父类Television生成的对象是没有constructor属性,所以给他加上(为啥没有?我就不用再详细说了吧)

[优点]

  • 实例是子类的实例,实际上也是父类的一个实例
  • 父类新增原型方法/原型属性,子类都能访问到

[缺点]

  • 子类实例共享属性,造成实例间的属性会相互影响

二、构造继承

这个就比较简单啦,上代码:

function  Television  () {
  this.name =  '电视'
  this.telecontroller =  '遥控器'
}

Television.prototype.play =  '播放'

function  Computer  () {
  // 在这里直接调用父类的方法修改父类的this上下文
  Television.call(this)
  this.name =  '电脑'
}

Computer.prototype.compute =  '计算'

console.log(new Computer().name)  // 电脑
console.log(new Computer().compute)  // 计算
console.log(new Computer().play)  // undefined
console.log(new Computer().telecontroller) // 遥控器
console.log(Computer.prototype.name)  // undefined

[优点]

  • 简单明了,直接继承父类构造函数的属性及方法

[缺点]

  • 无法继承父类原型链上的方法(new Computer().play //undefined

三、组合继承

原型链继承与构造继承相结合各取所长,看以下实现:

function  Television  () {
  this.name =  '电视'
  this.telecontroller =  '遥控器'
}

Television.prototype.play =  '播放'

function  Computer  () {
  Television.call(this)
  this.name =  '电脑'
}

Computer.prototype =  new Television()

Computer.prototype.constructor = Computer

Computer.prototype.compute =  '计算'

console.log(new Computer().name)  // 电脑
console.log(new Computer().compute)  // 计算
console.log(new Computer().play)  // 播放
console.log(new Computer().telecontroller) // 遥控器
console.log(Computer.prototype.name)  // 电视

[优点]

  • 子类既能继承父类构造函数中的属性和方法,又能继承父类原型链上的方法

[缺点]

  • 子类会拥有父类的两份属性,只是子类属性将其覆盖了而已

四、原型式继承

原型式继承其实就是定义一个方法,传入Object,并不需要定义一个类,传入参数obj返回一个继承obj对象的新对象,实现如下:

首先我们创建一个objectExtends方法

var  obj = {
  name: '电视',
  telecontroller: '遥控器'
}

function  objectExtends(obj){
  function  F(){}
  F.prototype = obj
  return  new F()
}

console.log(new objectExtends(obj).name)  // 电视
console.log(new objectExtends(obj).telecontroller)  // 遥控器

[优点]

  • 简单易懂,直接通过对象生成一个继承该对象的新对象

[缺点]

  • 不是类式继承,缺少类的概念

五、寄生式继承

这个名词很厉害,说白了就是原型式继承的加强版,同样传入Object,并不需要定义一个类,传入参数obj返回一个继承obj对象的新对象,然后在另一方法中以其他方式增强这个obj对象,返回最终的对象, 实现如下:

首先我们创建一个objectExtendsobjectEnhance方法

objectExtends方法作用依旧是实现原型式继承
objectEnhance方法就是在objectExtends方法的基础上对新的obj对象扩展他的原型方法而已

var  obj = {
  name: '电视',
  telecontroller: '遥控器'
}

function  objectExtends(oldObj){
  function  F(){}
  F.prototype = obj
  
  return  new F()
}

function  objectEnhance(obj){
  var  newObj = objectExtends(obj)

  newObj.property =  '扩展一个方法出来'

  return newObj
}

console.log(new objectEnhance(obj).name)  // 电视
console.log(new objectEnhance(obj).telecontroller)  // 遥控器
console.log(new objectEnhance(obj).property)  // 扩展一个方法出来

[优点]

  • 相比原型式继承更加强悍,增强了原型式继承的能力

[缺点]

  • 和原型式继一样,缺少类的概念

六、寄生组合式继承

看名字就知道寄生组合式继承就是结合了寄生式继承组合式继承,也是继承的终极解决方案

function  Television  () {
  this.name =  '电视'
  this.telecontroller =  '遥控器'
}

Television.prototype.play =  '播放'

function  Computer  () {
  Television.call(this)
  this.name =  '电脑'
}

Computer.prototype.compute =  '计算'

function  inheritPrototype(Father,Son){
  var  newPrototype = Object.create(Father.prototype)

  newPrototype.constructor = Son
  Son.prototype = newPrototype
}

inheritPrototype(Television, Computer)

console.log(new Computer().name)  // 电脑
console.log(new Computer().play)  // 播放
console.log(new Computer().telecontroller)  // 遥控器

[优点]

  • 很完美

[缺点]

  • 很繁琐

七、ES6中的继承

ES6的话就比较简单了,如果有不了解的可以去看看阮一峰的ECMAScript 6入门

class  Television {
  constructor  () {
    this.name =  '电视'
    this.telecontroller =  '遥控器'
  }

  play  () {
    alert('播放')
  }
}

class  Computer  extends Television{
  constructor  () {
    super()
    this.name =  '电脑'
  }

  compute  () {
    alert('计算')
  }
}

console.log(new Computer().name)  // 电脑
console.log(new Computer().play)  // ƒ play() { alert('播放') }
console.log(new Computer().telecontroller)  // 遥控器

[注意]

  • 如果你的子类重写了constructor方法,记得调用super,以确保父类构造逻辑运行

[优点]

  • 和寄生组合式继承实现的效果一致

最后献上两张关于ES5和ES6中继承的图片

所以ES6和ES5的继承是一模一样的,只是多了classextends,ES6的子类和父类,子类原型和父类原型,通过__proto__连接

End~

你可能感兴趣的:(面向对象从ES3到ES6的继承方法总结以及对比)