跳至主要內容

2. 继承

鸭梨大约 1 分钟

2. 继承

2.1 原型链继承

function Animal() {
  this.colors = ['black', 'white']
}

Animal.prototype.getColor = function () {
  return this.colors
}

function Dog() { }

Dog.prototype = new Animal()

function test() {
  const dog1 = new Dog()
  dog1.colors.push('brown')
  const dog2 = new Dog()
  console.log(dog2.colors)
  // ['black', 'white', 'brown']
}

test()

原型链继承存在的问题:

  1. 原型中包含的引用类型属性将被所有实例共享
  2. 子类在实例化的时候不能给父类构造函数传参

2.2 借用构造函数实现继承

function Animal(name) {
  this.name = name
  this.getName = function () {
    return this.name
  }
}

function Dog(name) {
  Animal.call(this, name)
}

Dog.prototype = new Animal()

借用构造函数实现继承解决了原型链继承的两个问题:引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法。

2.3 组合继承

function Animal(name) {
  this.name = name
  this.colors = ['black', 'white']
}

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

function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}

Dog.prototype = new Animal()
Dog.prototype.constructor = Dog

function test() {
  const dog1 = new Dog('奶昔', 2)
  dog1.colors.push('brown')
  const dog2 = new Dog('哈赤', 1)
  console.log(dog2)
  // Dog { name: "哈赤", colors: ["black", "white"], age: 1 }
}

test()

组合继承结合了原型链和借用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

2.4 寄生式组合继承

function Animal(name) {
  this.name = name
  this.colors = ['black', 'white']
}

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

function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}

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

function inheritPrototype(child, parent) {
  const prototype = object(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}

inheritPrototype(Dog, Animal)

组合继承已经相对完善了,但还是存在问题,它的问题就是调用了两次父类构造函数,第一次是在 new Animal(),第二次是在 Animal.call() 这里。

所以解决方案就是不直接调用父类构造函数给子类原型赋值,而是通过创建空函数 F 获取父类原型的副本。

上方 12~22 行代码可替换为下面的寄生式组合式的代码:

Dog.prototype =  Object.create(Animal.prototype)
Dog.prototype.constructor = Dog

2.5 ES6 class 语法

class Animal {
  constructor(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

export class Dog extends Animal {
  constructor(name, age) {
    super(name)
    this.age = age
  }
}