書接上文從 babel 看 class(上),第一篇說了 class 法通過 babel 會轉化成什麼樣子,而這篇則主要介紹繼承。es6
在 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
咱們用 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
子類的proto屬性,表示構造函數的繼承,老是指向父類;this
子類 prototype 屬性的proto屬性,表示方法的繼承,老是指向父類的 prototype 屬性;es5
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 的轉化過程以下:
源代碼太長就不放了,若是有興趣能夠本身打開babel 官網把上面代碼複製進來,就能夠看到完整代碼