從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 官網把上面代碼複製進來,就能夠看到完整代碼

相關文章
相關標籤/搜索