书接上文从 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 官网把上面代码复制进来,就能够看到完整代码