整理 JavaScript 中的几种继承模式

237 阅读4分钟

原型链继承

所谓的原型链继承就是把一个构造函数的原型对象指向另一个构造函数的实例对象。原型中的属性和方法都是共享的,所以这种继承模式的缺陷也很明显,就是当原型中包含引用类型的时候,引用值会在所以实例间共享。

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

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

function SubType(params) {}

SubType.prototype = new SuperType() // 将SubType的原型指向SuperType的实例

const instance = new SubType()
instance.colors.push('black')

const anotherInstance = new SubType()
anotherInstance.colors.push('pink')

console.log(instance.colors) // [ 'red', 'blue', 'green', 'black', 'pink' ]
console.log(anotherInstance.colors) // [ 'red', 'blue', 'green', 'black', 'pink' ]

原型中的属性和方法都是共享的,所以这种继承模式的缺陷也很明显,就是当原型中包含引用类型的时候,引用值会在所以实例间共享。原型链继承的第二个问题是子类型在实例化时不能给父类型的构造函数传参。所以一般不会单独使用原型链继承。

构造函数继承

构造函数继承的基本思路就是在子类构造函数中调用父类构造函数。

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

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

function SubType(params = []) {
  // 继承 SuperType
  // 这里也可以传递参数
  SuperType.call(this, ...params)
}

const instance = new SubType()
instance.colors.push('black')

const anotherInstance = new SubType()
anotherInstance.colors.push('pink')

console.log(instance.colors) // [ 'red', 'blue', 'green', 'black']
console.log(anotherInstance.colors) // [ 'red', 'blue', 'green', pink' ]

构造函数继承的主要缺点就是函数不能重用,子类不能访问父类原型上定义的方法。所以也不会单独使用构造函数继承。

组合继承

组合继承(伪经典继承),是将原型链继承和构造函数继承的优点结合起来。基本实现思路是使用原型链继承原型上的属性和方法(这部分是实例间共享的),而通过调用构造函数继承实例属性。

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

SuperType.prototype.getName = function () {
  return this.name
}

function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name)
  this.age = age
}

// 继承方法
SubType.prototype = new SuperType()

SubType.prototype.getAge = function () {
  return this.age
}

const instance = new SubType('Sugary', 12)
instance.colors.push('black')
console.log(instance.colors) // [ 'red', 'blue', 'green', 'black']
console.log(instance.getName()) // Sugary
console.log(instance.getAge()) // 12

const anotherInstance = new SubType('Ting', 20)
anotherInstance.colors.push('pink')
console.log(anotherInstance.colors) // [ 'red', 'blue', 'green', pink' ]
console.log(anotherInstance.getName()) // Ting
console.log(anotherInstance.getAge()) // 20

寄生组合式继承

组合继承存在最主要的效率问题就是父类构造函数始终会被调用两次,一次是在创建子类原型时调用,另一次是在子类构造函数中调用。

......
function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name) // 第二次调用SuperType
  this.age = age
}

// 继承方法
SubType.prototype = new SuperType() // 第一次调用SuperType
......

第一次调用 SuperType 的时候,SubType 的原型对象继承了 SuperType 的所有属性,name 和 age 属性此时存在于 SubType.prototype 对象中。而当第二次调用 SuperType 的时候,SubType 的实例对象继承了 SuperType 构造函数中的属性,name 和 age 属性此时存在于 SubType 的实例对象中,屏蔽了SubType 原型中的 name 和 age 属性。所以说,存在于SubType 原型中的 name 和 age 属性其实是没必要的。我们只需要复制父类构造函数的原型对象的副本,并把它复制给子类的原型对象即可。 这就是典型的寄生组合式继承

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

SuperType.prototype.getName = function () {
  return this.name
}

function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name)
  this.age = age
}

// 继承方法
// SubType.prototype = new SuperType()

SubType.prototype = Object.create(SuperType.prototype)

SubType.prototype.getAge = function () {
  return this.age
}

const instance = new SubType('Sugary', 12)
instance.colors.push('black')
console.log(instance.colors) // [ 'red', 'blue', 'green', 'black']
console.log(instance.getName()) // Sugary
console.log(instance.getAge()) // 12

const anotherInstance = new SubType('Ting', 20)
anotherInstance.colors.push('pink')
console.log(anotherInstance.colors) // [ 'red', 'blue', 'green', pink' ]
console.log(anotherInstance.getName()) // Ting
console.log(anotherInstance.getAge()) // 20

上述代码中的 Object.create() 方法,设计的思想其实也是 JS 中的一种继承模式,叫原型式继承。

原型式继承

原型式继承适用于,你有一个对象,想在它的基础上再创建一个新的对象,然后再对返回的对象进行修改。由于是基于原型链的继承,所以返回的所有对象的属性都是共享的。

function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}

寄生式继承

寄生式继承的实现思路是创建一个对象,以某种方式增强对象,然后返回这个对象。

function createAnother(original) {
  let clone = new Object()
  clone.sayHi = function () {
    console.log('hi')
  }
  return clone
}

const person = {
  name: 'Sugary',
}

const anotherPerson = createAnother(person)

anotherPerson.sayHi() // hi

总结

JavaScript 中的几种继承模式:

  1. 原型链继承
  2. 构造函数继承
  3. 组合继承
  4. 寄生式组合继承
  5. 原型式继承
  6. 寄生式继承