Skip to content

继承

我们需要掌握 ES5 的继承方式与 ES6 的方式

ES5 的继承

1、 原型链的方式

javascript
function Parent(name) {
  this.name = name
  this.type = "parent"
}
Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]

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

Child.prototype = new Parent()

var teacher1 = new Child("张三")
var teacher2 = new Child("李四")

console.log(teacher1)
console.log(teacher1.getName()) //可以调用Person的实例方法与属性,且实例方法中的this指向子对象
console.log(teacher1.constructor === Child) // false
console.log(teacher1.constructor === Parent) // true

问题:

  1. 调用父级构造方法的时候不能赋值

  2. Child 的原型其实指向 Parent 的一个同一个实例对象,所以所有的 Child 的实例对象共享同一个实例

  3. Child 的实例对象的构造函数指向 Parent

javascript
teacher1.arr[2] = 100
console.log(teacher2.arr) // [1,2,100,4]

2. 构造函数继承

为了解决上述所有的子实例对象共享同一个父实例对象的问题,其使用了 Parent.call(this, ...args)

ts
function Parent(name) {
  this.name = name
  this.type = "parent"
}
Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]

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

var teacher1 = new Child("张三")
var teacher2 = new Child("李四")

console.log(teacher1)
console.log(teacher1.constructor === Child) // true
console.log(teacher1.constructor === Parent) // false
// console.log(teacher1.getName()) // 错误

其解决了上述的 3 个问题,但是引发了一个新的问题,

缺点:

  1. 子实例对象无法继承父对象原型上的属性和方法

3. 组合式继承

通过上述两种方式的集合,形成了一个新的组合式继承

ts
function Parent(name) {
  this.name = name
  this.type = "parent"
}
Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]

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

Child.prototype = new Parent()
// 修改子对象的构造函数的指向
Child.prototype.constructor = Parent

var teacher1 = new Child("张三")
var teacher2 = new Child("李四")

console.log(teacher1)
console.log(teacher1.constructor === Child) // true
console.log(teacher1.constructor === Parent) // false
console.log(teacher1.getName())

问题:

  1. 父对象被执行了两次

  2. Child.prototype = new Parent() 修改了子函数的原型,导致自身的原型对象可能被覆盖

4. 寄生式继承

其借用工厂模式和原型链,从而实现了继承的概念

ts
function Parent(name) {
  this.name = name
  this.type = "parent"
}
Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]

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

function child(name, age) {
  var newChild = clone(Parent)
  newChild.name = name
  return newChild
}

var teacher1 = child()

缺点

  1. 不再具备构造函数的概念
  2. 不能实现函数的复用

5. 寄生组合式继承

ts
function Parent(name) {
  this.name = name
  this.type = "parent"
}
Parent.prototype.getName = function () {
  return this.name
}
Parent.prototype.arr = [1, 2, 3, 4]

function Child(name, age) {
  Parent.call(this, ...arguments)
  this.name = name
}
Child.prototype = Object.create(Parent.prototype, {
  constructor: {
    value: Child,
  },
})
var teacher1 = new Child("张三")
var teacher2 = new Child("李四")

console.log(teacher1)
console.log(teacher1.constructor === Child) // false
console.log(teacher1 instanceof Parent) // true
console.log(teacher1.getName()) //可以调用Person的实例方法与属性,且实例方法中的this指向子对象

ES6 的继承

可以使用 extends 进行继承,其核心也是借鉴寄生组合式继承的理念

js
class Animal {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

class Dog extends Animal {
  constructor(name, age) {
    super(name, age)
  }
}

然后我们看 ES6 转换成 ES5 的结果

js
"use strict"

function _typeof(obj) {
  "@babel/helpers - typeof"
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj
    }
  } else {
    _typeof = function _typeof(obj) {
      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype
        ? "symbol"
        : typeof obj
    }
  }
  return _typeof(obj)
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function")
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true },
  })
  if (superClass) _setPrototypeOf(subClass, superClass)
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p
      return o
    }
  return _setPrototypeOf(o, p)
}

function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct()
  return function () {
    var Super = _getPrototypeOf(Derived),
      result
    if (hasNativeReflectConstruct) {
      var NewTarget = _getPrototypeOf(this).constructor
      result = Reflect.construct(Super, arguments, NewTarget)
    } else {
      result = Super.apply(this, arguments)
    }
    return _possibleConstructorReturn(this, result)
  }
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call
  }
  return _assertThisInitialized(self)
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
  }
  return self
}

function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false
  if (Reflect.construct.sham) return false
  if (typeof Proxy === "function") return true
  try {
    Date.prototype.toString.call(Reflect.construct(Date, [], function () {}))
    return true
  } catch (e) {
    return false
  }
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o)
      }
  return _getPrototypeOf(o)
}

function _instanceof(left, right) {
  if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
    return !!right[Symbol.hasInstance](left)
  } else {
    return left instanceof right
  }
}

function _classCallCheck(instance, Constructor) {
  if (!_instanceof(instance, Constructor)) {
    throw new TypeError("Cannot call a class as a function")
  }
}

var Animal = function Animal(name, age) {
  _classCallCheck(this, Animal)

  this.name = name
  this.age = age
}

var Dog = /*#__PURE__*/ (function (_Animal) {
  _inherits(Dog, _Animal)

  var _super = _createSuper(Dog)

  function Dog(name, age) {
    _classCallCheck(this, Dog)

    return _super.call(this, name, age)
  }

  return Dog
})(Animal)
  1. 怎么将 Aninal 的原型对象继承给 Dog,看上面的代码其是通过 _inherits(Dog, _Animal);
js
/**
 * subClass  子构造函数
 * superClass  父构造函数
 */
function _inherits(subClass, superClass) {
  // 错误判断
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function")
  }
  // 将子构造函数的 prototype 指向父构造函数的 prototype, 并且重写 constructor
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true },
  })

  if (superClass) _setPrototypeOf(subClass, superClass)
}

function _setPrototypeOf(o, p) {
  // setPrototypeOf 的profill语法
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p
      return o
    }
  // 修改 subClass 的原型对象指向 superClass
  return _setPrototypeOf(o, p)
}
  1. 处理 super 语法

对于 Super 其作用主要是以下方面

  • 执行父 Class 对象的构造函数

  • 继承父 Class 构造函数原型链上的属性

js
// var _super = _createSuper(Dog);
function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false
  if (Reflect.construct.sham) return false
  if (typeof Proxy === "function") return true
  try {
    Date.prototype.toString.call(Reflect.construct(Date, [], function () {}))
    return true
  } catch (e) {
    return false
  }
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call
  }
  return _assertThisInitialized(self)
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
  }
  return self
}

function _createSuper(Derived) {
  // 判断是否支持 ES6的 Reflect.construct 方法
  var hasNativeReflectConstruct = _isNativeReflectConstruct()
  return function () {
    var Super = _getPrototypeOf(Derived),
      result
    if (hasNativeReflectConstruct) {
      // 获取其原型链上的 constructor
      var NewTarget = _getPrototypeOf(this).constructor
      // 对
      result = Reflect.construct(Super, arguments, NewTarget)
    } else {
      result = Super.apply(this, arguments)
    }
    return _possibleConstructorReturn(this, result)
  }
}