js 继承方式举例

96 阅读2分钟

js 继承方式举例

原型链继承

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

function Dog() { }
Dog.prototype = new Animal()

const dog1 = new Dog()
const dog2 = new Dog()
dog1.colors.push('brown') // 通过 dog1.__proto__.colors 改变了 Dog.prototype.colors
console.log('dog1---', dog1)
console.log('dog2.colors---', dog2.colors)  // ["black", "white", "yellow", "brown"]

原型链继承存在的问题:

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

借用构造函数实现继承

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

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

const dog1 = new Dog('Tom')
console.dir(Animal)
console.log('dog1---', dog1)
  1. 借用构造函数实现继承解决了原型链继承的两个问题:引用类型属性共享问题,以及子类实例化不能传参问题。
  2. 但是由于方法定义在构造函数内,所以会导致每次创建实例都会创建一遍方法。

组合继承

  1. 组合继承结合了原型链和借用构造函数,将两者的优点集中了起来。
  2. 基本的思路是使用原型链继承原型上的方法,而通过借用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。
function Animal(name) {
  this.name = name
  this.colors = ['black', 'white']
}
Animal.prototype.getName = function () {
  return this.name
}
console.log(Animal.prototype)

function Dog(name, age) {
  Animal.call(this, name)
  this.age = age
}
Dog.prototype = new Animal() // Dog 的实例可以根据 __proto__ 找到 Dog.prototype,Dog.prototype(Animal的实例)可以根据 __proto__ 找到 Animal.prototype
Dog.prototype.constructor = Dog

let dog1 = new Dog('奶昔', 2)
dog1.colors.push('brown')
let dog2 = new Dog('哈赤', 1)
console.log(dog2.getName())

寄生式组合继承

  1. 组合继承已经相对完善了,但还是存在问题,它的问题就是调用了 2 次父类构造函数,第一次是在 new Animal(),第二次是在 Animal.call() 这里。
  2. 所以解决方案就是不直接调用父类构造函数给子类原型赋值,而是通过创建空函数 F 获取父类原型的副本。
  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
}

function F() { }
F.prototype = Animal.prototype
const f = new F()
f.constructor = Dog
Dog.prototype = f

let dog1 = new Dog('奶昔', 2)
dog1.colors.push('brown')
let dog2 = new Dog('哈赤', 1)
console.log(dog2.getName())

稍微封装下上面添加的代码后:

function inheritPrototype(Child, Parent) {
  const prototype = getPrototype(Parent.prototype)
  prototype.constructor = Child
  Child.prototype = prototype
}

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

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
}
inheritPrototype(Dog, Animal)

如果你嫌弃上面的代码太多了,还可以基于组合继承的代码改成最简单的寄生式组合继承:

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 = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog

Object.create()是ES5才有的特性,不支持IE8及更早的版本的IE。

多重继承

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

function Dog(voice) {
  this.voice = voice
}
Dog.prototype.speak = function () {
  return this.voice
}

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

Bulldog.prototype = Object.create(Animal.prototype)
Object.assign(Bulldog.prototype, Dog.prototype)
Bulldog.prototype.constructor = Bulldog
// Bulldog 继承了两个类

const aaa = new Bulldog('斗牛犬', '汪汪汪')
console.log(aaa.getName())
console.log(aaa.speak())

class 实现继承

class Animal {
  constructor(name) {
    this.name = name
  }
  getName() {
    return this.name
  }
}
console.dir(Animal)

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