ES5构造函数之间的继承方法

ES5继承

原型链继承

function SuperType() {
  this.property = true
}
SuperType.prototype.getSuperValue = function() {
  return this.property
}
function SubType() {
  this.subproperty = false
}
SubType.prototype = new SuperType() // 原型链继承
SubType.prototype.getSubValue = function() {
  return this.subproperty
}
let instance = new SubType()
console.log(instance.getSuperValue()) // true

缺点:

  1. 实例无法向父类构造函数传参
  2. 继承单一
  3. 所有新实例都会共享父类实例的属性(原型上的属性共享,一个是你修改了原型属性,另一个实例的原型属性也会被修改)

    借用构造函数继承

    function Person(name) {
      this.name = name
      this.sum = function() {
        alert(this.name)
      }
    }
    function Con() {
      // 利用call apply在函数内部将父类构造函数引入子类函数
      Person.call(this, 'name')
      this.age = 12
    }
    var con1 = new Con()
    console.log(con1.name)    // name
    console.log(con1.age) // 12
    console.log(con1 instanceof Person) // false

    特点:

  4. 可以传参
  5. 可以继承多个构造函数属性

缺点:

  1. 只能继承父类构造函数的属性
  2. 无法实现构造函数的复用
  3. 每个新实例都有父类构造函数的副本

组合继承

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

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

function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name)  // 第一次调用SuperType
  this.age = age
}
// 继承方法
SubType.prototype = new SuperTuper('Nich', 29) // 第二次调用SuperType
SubType.prototype.sayAge = function() {
  console.log(this.age)
}

let instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors) // "red, blue, green, black"
instance1.sayName() // 'Nich'
instance1.sayAge() // 29

let instance2 = new SubType('Greg', 27)
console.log(instance2.colors) // "red, blue, green"
instance2.sayName() // 'Greg'
instance2.sayAge()  // 27

组合继承弥补了原型链和盗用构造函数的不足,是JS中使用最多的集成模式
组合继承也保留了instanceof操作符和isPrototypeOf()的识别合成对象的能力
组合继承的效率问题:父类构造函数始终会被调用两次,一次是在创建子类原型时调用,另一次时在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就可以了

原型式继承与Object.create()

// 当Object.create()只传一个参数时,与以下效果相同
function object(o) {
  function F(){}
  F.prototype = o
  return new F()
}
let person = {
  name: 'Nich',
  friends: ['Van', 'Court']
}
let anotherPerson = object(person)
// 等同于let anotherPerson = Object.create(person)
console.log(anotherPerson.name) // Nich (通过原型链向上查找)
anotherPerson.name = 'Greg'
anotherPerson.friends.push('Rob')

let yetAnotherPerson = object(person)
yetAnotherPerson.name = 'Linda'
yetAnotherPerson.friends.push('Barbie')

console.log(person.friends) // Court, Van, Rob, Barbie

Object.create()的第二个参数与Object.defineProperties第二个参数一样: 每个新增属性都通过各自的描述符来描述,以这种方式添加的属性会遮蔽原型上的同名属性

let person = {
  name: 'Nich',
  friends: ['Van', 'Court']
}
let anotherPerson = Object.create(person, {
  name: {
    value: 'Greg'
  }
})
console.log(anotherPerson.name) // 'Greg'

原型式继承非常适合不需要单独创建构造函数,担任需要在对象间共享信息的场合,但是属性中包含的引用值始终会在相关对象间共享,和使用原型模式式一样的

寄生式继承

寄生式继承背后的思路类似于寄生构造函数和工厂模式:
创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象

此函数接收一个参数,就是新对象的基准对象
function createAnother(original) {
  let clone = object(original)
  clone.sayHi = function() {
    console.log('hi')
  }
  return clone
}
let person = {
  name: 'Nick',
  friends: ['Bob', 'Van']
}
let anotherPerson = createAnother(person)
anotherPerson.sayHi() // hi

通过寄生式继承给对象添加函数会导致函数难以重用, 与构造函数模式类似

寄生式组合继承

function inheritPrototype(subType, superType) {
  let prototype = object(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function() {
  console.log(this.age)
}

寄生式组合继承避免了多次调用SuperType构造函数,避免了SubType.prototype上不必要也用不到的属性,因此效率更高。而且原型键保持不变,instanceof和isPrototypeOf()依然有效
寄生式组合继承可以算是引用类型继承的最佳模式

你可能感兴趣的:(javascript前端)