Skip to content

JS 实现继承的几种方式 #49

Open
@myLightLin

Description

@myLightLin

在《JS 高级程序设计》这本书里列举了 JS 实现继承的多种方式以及对应的优缺点。

原型链继承

原型链继承通过 new 操作符将子类的原型和父类的原型链接起来。代码示例如下:

function Parent() {
  this.name = 'parent'
}
Parent.prototype.getName = function() {
  console.log(this.name)
}
function Child() {
}
Child.prototype = new Parent()
var child = new Child()
console.log(child.getName)  // parent

缺点:

  • 引用类型的属性在所有实例间共享,修改一个实例值影响其它
  • 创建子类实例时不能向父类传参

构造函数继承

通过在子类构造函数借用 call 调父类来实现,代码如下:

function Parent() {
  this.name = 'parent'
}

function Child() {
  Parent.call(this)
}
var child = new Child()
console.log(child.name)  // parent

缺点:

  • 每次创建实例都会创建一遍构造函数中的方法
  • 不能做到属性方法共享

组合继承

把原型链继承和构造函数继承组合起来了:

function Parent(name) {
  this.name = name
}
Parent.prototype.getName = function() {
  console.log(this.name)
}
function Child(name, age) {
  Parent.call(this)
  this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child = new Child('child', 18)
console.log(child.name)  // child
console.log(child.age)  // 18

缺点:

  • 调用了两次父类

原型式继承

这种继承就是 ES5 Object.create 的模拟实现:

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

缺点:

  • 与原型链一样,引用类型属性的值始终会在实例间共享

寄生式继承

创建一个用于实现继承的函数,函数内部新建一个对象,以某种形式增强对象后返回它,代码如下:

function createObj(o) {
  var clone = Object.create(o)
  clone.sayName = function() {
    console.log('say hi')
  }
  return clone
}

缺点:

  • 每次调用都会创建一遍方法

寄生组合式继承

这是对 组合继承 的改进,是一种完美实现 JS 继承的解决方案。组合继承最大的缺点在于调用了两次父类构造函数:

// 第一次
Child.prototype = new Parent()
// 第二次
Parent.call(this)

这带来的问题是子类实例和子类构造函数的 prototype 上会有多余的属性。为了精益求精,避免两次调用所带来的浪费,解决方法是不让子类原型与父类实例挂钩,而是让子类原型间接地访问到父类原型,这其中的关键就是前面的 原型式继承:通过一个空函数来中转,让子类原型直接去 new 空函数,而空函数的原型又是父类,这样就间接访问到了父类原型。寄生组合式的代码示例如下:

function Parent(name) {
  this.name = name
}
Parent.prototype.getName = function () {
  console.log(this.name)
}

function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}

function inherit(child, parent) {
  function F() {}
  F.prototype = parent.prototype
  child.prototype = new F()
}
inherit(Child, Parent)
Child.prototype.constuctor = Child

var child = new Child('child', 18)
console.log(child)   // {name: 'child',  age: 18}

寄生组合式继承是比较完美的继承方式。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions