从babel看class(下)

书接上文从 babel 看 class(上),第一篇说了 class 法通过 babel 会转化成什么样子,而这篇则主要介绍继承。es6

es5

在 es5 下咱们经常使用的就是寄生组合式继承了,下面就是一个例子,看下 es5 的实现步骤express

function Foo() {
  this.name = "foo";
}
Foo.prototype.getName = function() {
  return this.name;
};
function Extends(name) {
  Foo.call(this);
  this.name = name;
}
Extends.prototype = Object.create(Foo.prototype);
Extends.prototype.construcotr = Extends;

var child = new Extends("Extends");
child.getName(); // "Extends"
复制代码

上面对应的原型图为 babel

back

es6

咱们用 class 的形式来重写上面的例子函数

class Foo {
  name = "foo";
  getName() {
    return this.name;
  }
}
class Extends extends Foo {
  constructor(name) {
    super();
    this.name = name;
  }
}
var child = new Extends("Extends");
child.getName(); // "Extends"
复制代码

class 经过 extends 实现继承,上面调用了super这是必须的,至关于Foo.call(this),若是没有调用就使用 this 会报错,由于子类的 this 必须经过父类的 this 来构建,extends 还能够继承父类的静态属性和方法post

class Foo {
  name = "foo";
  getName() {
    return this.name;
  }
  static getName() {
    return "Foo";
  }
}
class Extends extends Foo {
  constructor(name) {
    super();
    this.name = name;
  }
  static get() {
    return super.getName();
  }
}
var child = new Extends("Extends");
console.log(Extends.get()); //Foo

child.getName(); // "Extends"
复制代码

出现这种结果的缘由是 class 存在两条继承链ui

  1. 子类的proto属性,表示构造函数的继承,老是指向父类;this

  2. 子类 prototype 属性的proto属性,表示方法的继承,老是指向父类的 prototype 属性;es5

1

babel

babel 翻译以后代码有点多,我先精简一下只看核心部分spa

var Foo =
  /*#__PURE__*/
  (function() {
    function Foo() {
      _classCallCheck(this, Foo);

      _defineProperty(this, "name", "foo");
    }

    _createClass(
      Foo,
      [
        {
          key: "getName",
          value: function getName() {
            return this.name;
          }
        }
      ],
      [
        {
          key: "getName",
          value: function getName() {
            return "Foo";
          }
        }
      ]
    );

    return Foo;
  })();

var Extends =
  /*#__PURE__*/
  (function(_Foo) {
    _inherits(Extends, _Foo);

    function Extends(name) {
      var _this;

      _classCallCheck(this, Extends);

      _this = _possibleConstructorReturn(
        this,
        _getPrototypeOf(Extends).call(this)
      );
      _this.name = name;
      return _this;
    }

    _createClass(Extends, null, [
      {
        key: "get",
        value: function get() {
          return _get(_getPrototypeOf(Extends), "getName", this).call(this);
        }
      }
    ]);

    return Extends;
  })(Foo);

var child = new Extends("Extends");
console.log(Extends.get()); //Foo

child.getName(); // "Extends"
复制代码

Foo这部分_classCallCheck_defineProperty_createClass在上一篇有介绍,主要就是给属性赋值,这里跳过,主要来看Extends部分prototype

首先执行了_inherits函数,这个函数展开

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);
}
复制代码

实际上就是验证和重写继承链,extends按照规范能够是一个函数或者 null,这里判断没什么好说的;

Object.setPrototypeOf若是存在就用这个,不存在直接用__proto__属性,重写了prototype和子类的原型。 再来看下这段代码

var _this;

_classCallCheck(this, Extends);

_this = _possibleConstructorReturn(this, _getPrototypeOf(Extends).call(this));
复制代码

首先定义了一个_this的变量,还记得咱们以前说 class 的继承是先经过父类的构造函数完成塑造,获得与父类一样的实例属性和方法,而后再对其进行加工,加上子类本身的实例属性和方法,若是不使用super调用 this 进行赋值之类就会报错。

_possibleConstructorReturn函数就是经过判断_this是否为undefined来决定是否显示报错

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;
}
复制代码

this, _getPrototypeOf(Extends).call(this);这段代码就至关于super从这一步 this 获得了父类一样的实例属性和方法,下面就是对子类进行加工。

总结

从整个执行过程来看,babel 的转化过程以下:

  1. 验证,若是不是 new 调用就报错,若是是继承还须要验证继承的是否为函数或者 null;
  2. 重写继承链,同时调用父类构造函数获得父类一样的实例属性和方法;
  3. 赋值,为构造函数自己和 this 赋值;

源代码太长就不放了,若是有兴趣能够本身打开babel 官网把上面代码复制进来,就能够看到完整代码

相关文章
相关标签/搜索