Skip to content
本页目录

对象继承模式

该篇内容是结合自己理解后总结自红宝书《JavaScript高级程序设计(第4版)》相关内容。

原型链继承

不能使用对象字面量创建原型方法,否则会切断原型关系,且创建的原型方法必须放在替换原型语句之后(代码高亮部分)。

JavaScript
function Anime() {
  this.name = '平泽唯'
}
Anime.prototype.getName = function () {
  return this.name
}
function Kon() {
  this.age = 14
}
Kon.prototype = new Anime()
Kon.prototype.info = function () {
  return this.getName() + '-' + this.age
}
const yui = new Kon()
yui.info()  // '平泽唯-14'
  • 优点:没有对象识别问题(instanceof操作符 、isPrototypeOf方法均能使用);
  • 缺点:
    1. 没有办法在不影响所有对象实例的情况下,给父类构造函数传递参数;
    2. 原型的共享性导致引用类型在实例中异常的共享数据(实例持有相同引用),如下例:
    JavaScript
    function Anime() {
      this.name = []
    }
    function Kon() { }
    Kon.prototype = new Anime()
    const yui = new Kon()
    yui.name.push('平泽唯')
    console.log(yui.name)  // ['平泽唯']
    const mio = new Kon()
    mio.name.push('秋山澪')
    console.log(mio.name)  // ['平泽唯', '秋山澪']

经典继承

经典继承即借用构造函数继承。

JavaScript
function Anime(name) {
  this.name = [name]
}
function Kon(name) {
  Anime.call(this, name)
}
const yui = new Kon('平泽唯')
yui.name.push(14)
console.log(yui.name)  // ['平泽唯', 14]
const mio = new Kon('秋山澪', 15)
mio.name.push(15)
console.log(mio.name)  // ['秋山澪', 15]
  • 优点:
    1. 解决了原型链继承中引用类型在实例中异常的共享性问题;
    2. 也解决了不能向父类构造函数传参的问题。
  • 缺点:方法都在构造函数(即便在原型中定义,对子类也不可见)中定义,复用性差。

组合继承

组合继承即组合使用原型链继承和经典继承。

JavaScript
function Anime(name) {
  this.name = [name]
}
Anime.prototype.getName = function () {
  return this.name.join('-')
}
function Kon(name, age) {
  Anime.call(this, name)
  this.age = age
}
Kon.prototype = new Anime()  // 第一次调用父类Anime
Kon.prototype.info = function () {
  return this.getName() + '-' + this.age
}
const yui = new Kon('平泽唯', 14)  // 第二次调用父类Anime
yui.name.push('樱丘高中')
yui.info()  // '平泽唯-樱丘高中-14'
const mio = new Kon('秋山澪', 15)
mio.info()  // '秋山澪-15'
  • 优点:结合了原型链继承和经典继承的优点的同时又巧妙的避免了两者的缺点;
  • 缺点:无论什么情况下,实例化对象时都会调用两次父类Anime构造函数。

原型式继承

原型式继承其实就是将传入的对象作为创建的对象的原型,也是Object.create()Polyfill

JavaScript
function createObj(obj) {
  function Fn() { }
  Fn.prototype = obj
  return new Fn()
}
const kon = {
  name: []
}
const yui = createObj(kon)
yui.name.push('yui')
console.log(yui.name)  // ['yui']
const mio = createObj(kon)
mio.name.push('mio')
console.log(mio.name)  // ['yui', 'mio']
  • 缺点:原型中引用类型在实例中异常的共享性问题依然存在。

寄生式继承

所谓寄生式继承就是创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

JavaScript
const kon = {
  name: []
}
function createObj(obj) {
  const _o = Object.create(obj)
  _o.info = function () {
    return _o.name.join('-')
  }
  return _o
}
const yui = createObj(kon)
yui.name.push('yui')
yui.info()  // 'yui'
const mio = createObj(kon)
mio.name.push('mio')
mio.info()  // 'yui-mio'
  • 缺点:原型中引用类型在实例中异常的共享性问题依然存在。

寄生组合式继承

即优化版的组合继承

JavaScript
function inherit(child, parent) {
  // 创建对象
  const prototype = Object.create(parent.prototype)
  // 增强对象
  prototype.constructor = child
  // 指定对象 
  child.prototype = prototype
}
function Anime(name) {
  this.name = [name]
}
Anime.prototype.getName = function () {
  return this.name.join('-')
}
function Kon(name, age) {
  Anime.call(this, name)
  this.age = age
}
inherit(Kon, Anime)
Kon.prototype.info = function () {
  return this.getName() + '-' + this.age
}
const yui = new Kon('平泽唯', 14)
yui.name.push('樱丘高中')
yui.info()  // '平泽唯-樱丘高中-14'
const mio = new Kon('秋山澪', 15)
mio.info()  // '秋山澪-15'
  • 优点:
    1. 不存在原型中引用类型在实例中异常的共享性问题;
    2. 没有对象识别问题(instanceof操作符 、isPrototypeOf方法均能使用);
    3. 只调用了一次Anime构造函数,并且因此避免了在Anime.prototype上面创建不必要的、多余的属性。