skip to content

JavaScript
JavaScript 继承

子类的继承其实就是继承父类的属性和方法

原型链继承

function Father() {
  this.colors = ['red', 'green', 'blue']
}

Father.prototype.getColors = function () {
  return this.colors
}

function Child(value) {
  this.value = value
}

Child.prototype = new Father() // 关键代码
Child.prototype.getValue = function () {
  return this.value
}

let c1 = new Child('c1')
let c2 = new Child('c2')
c1.colors[0] = null
c2.colors[0] // ==> null

缺点:多个实例对引用类型的操作会被篡改。

构造函数继承

function Father() {
  this.colors = ['red', 'green', 'blue']
}

Father.prototype.getColors = function () {
  return this.colors
}

function Child(value) {
  Father.call(this) // 关键代码
  this.value = value
}

Child.prototype.getValue = function () {
  return this.value
}

let c1 = new Child('c1')
let c2 = new Child('c2')
c1.colors[0] = null
c2.colors[0] // ==> "red"
c1.getColors() // ==> Uncaught TypeError: c1.getColors is not a function

缺点:

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现复用,每个子类都有父类实例函数的副本,影响性能

组合继承

function Father() {
  this.colors = ['red', 'green', 'blue']
}

Father.prototype.getColors = function () {
  return this.colors
}

function Child(value) {
  Father.call(this) // 关键代码
  this.value = value
}

Child.prototype = new Father() // 关键代码
Child.prototype.constructor = Child // 关键代码
Child.prototype.getValue = function () {
  return this.value
}

let c1 = new Child('c1')
let c2 = new Child('c2')
c1.colors.push('black')
console.log(c1.getColors()) // ==> ["red", "green", "blue", "black"]
console.log(c2.getColors()) // ==> ["red", "green", "blue"]
console.log(c1.getValue()) // c1
console.log(c2.getValue()) // c2

注意:

组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。

寄生组合式继承

function Father() {
  this.colors = ['red', 'green', 'blue']
}

Father.prototype.getColors = function () {
  return this.colors
}

function Child(value) {
  Father.call(this) // 关键代码
  this.value = value
}

// 下面的操作类似于
// Child.prototype.__proto__ = Father.prototype
Child.prototype = Object.create(Father.prototype) // 关键代码
Child.prototype.getValue = function () {
  return this.value
}

let c1 = new Child('c1')
let c2 = new Child('c2')
c1.colors.push('black')
console.log(c1.getColors()) // ==> ["red", "green", "blue", "black"]
console.log(c2.getColors()) // ==> ["red", "green", "blue"]
console.log(c1.getValue()) // c1
console.log(c2.getValue()) // c2

ES6 extends

class Father {
  colors = ['red', 'green', 'blue']
  getColors() {
    return this.colors
  }
}

class Child extends Father {
  constructor(value) {
    super()
    this.value = value
  }
  getValue() {
    return this.value
  }
}

let c1 = new Child('c1')
let c2 = new Child('c2')
c1.colors.push('black')
console.log(c1.getColors()) // ==> ["red", "green", "blue", "black"]
console.log(c2.getColors()) // ==> ["red", "green", "blue"]
console.log(c1.getValue()) // c1
console.log(c2.getValue()) // c2

class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)

当然了,之前也说了在 JS 中并不存在类,class 的本质就是函数。