Keep

类和继承 - JavaScript

发布:

在 JavaScript 中实现类继承,最简单的方式是通过 es6 中的关键字 class 和 extends. 下面代码是让类 S 继承类 P:

class P {
  constructor(a) {
    this.a = a;
  }
}

class S extends P {
  constructor(a, b) {
    super(a);
    this.b = b;
  }
}

new S(1, 2);

ps: 语法层在实现时,要求 S 中必须调用 super([])

下面看在不使用 class/extends 关键字情况下,如何实现 extends。 使用 babel(可直接使用在线工具 https://babeljs.io/repl/)转语法,然后分析转换的结果。

// 为了保证可以继承父类的 (非静态) 属性
// 如果父类返回对象,就直接使用此对象作为实例
// 否则,使用当前上下文 (在这里是 self -> this)
// note:
// 在 JavaScript 中,是支持返回对象做实例的
// 建议查看相应资料来了解 JavaScript 中的类定义方式
function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return call && (typeof call === "object" || typeof call === "function")
    ? call
    : self;
}

// 继承的主要实现
function _inherits(subClass, superClass) {
  // 保证父类只可能是 null 或 function - 其它类型没意义
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError(
      "Super expression must either be null or a function, not " +
        typeof superClass
    );
  }
  // 把子类原型重写为一个对象
  // 并且此对象的 __proto__ 属性指定 superClass.prototype
  // 以保证原型链指向
  // 比如:
  // subclass.__proto__ -> subClass.prototype
  // subClass.prototype.__proto__ -> superClass.prototype
  // 最终让 subClass 实例可访问到 superClass 原型上的方法/属性
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    // 维持 class.prototype.constructor === class
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  // 让子类在继承父类属性/方法 - 静态
  // 子类当作实例,实例的 __proto__ 属性指向一对象,
  // 那么实例可直接访问对象上的属性/方法
  if (superClass)
    Object.setPrototypeOf
      ? Object.setPrototypeOf(subClass, superClass)
      : (subClass.__proto__ = superClass);
}

// 用来检查类调用方式
// 仅支持 new className 方式调用
// 不像 jQuery 等,直接内部转换
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var P = function P(a) {
  _classCallCheck(this, P);

  this.a = a;
};

var S = (function(_P) {
  _inherits(S, _P);

  function S(a, b) {
    _classCallCheck(this, S);

    var _this = _possibleConstructorReturn(
      this,
      // 直接使用:
      // _P.call(this, a)
      // 有什么区别?
      (S.__proto__ || Object.getPrototypeOf(S)).call(this, a)
    );

    _this.b = b;
    return _this;
  }

  return S;
})(P);

new S(1, 2);

avatar image

@read2025, 生活在北京(北漂),程序员,宅,马拉松[纯粹],喜欢动漫。"骑士总能救出公主,是因为恶龙从没伤害过她"