-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
ES6 系列之 Babel 是如何编译 Class 的(下) #106
Comments
打卡~ |
好奇 es6 多了这一个步骤的原因,继承静态属性和方法吗? |
ES5 的寄生组合式继承,应该漏掉了一步修复构造器的指向 Child.prototype.constructor = Child; |
除此之外也可以通过构造方法(类)来判断两个“类”是否是继承关系吧,比如: Child instanceof Parent // true |
大佬们,这一句什么情况会执行到啊? if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
} |
应该是子类的构造方法里面没有使用super不能使用this |
instanceof不行吧,A instanceof B 是判断A的__proto__是否和B.prototype相等,一直查到A.__proto__为null时,这里只是 |
应该是child的实例为true |
为什么没有调用super,不能使用this呢,难道new的时候,没有创建this吗 |
看下 babel 编译后的代码,就明白了。
|
Child instanceof Parent // false |
前言
在上一篇 《 ES6 系列 Babel 是如何编译 Class 的(上)》,我们知道了 Babel 是如何编译 Class 的,这篇我们学习 Babel 是如何用 ES5 实现 Class 的继承。
ES5 寄生组合式继承
原型链示意图为:
关于寄生组合式继承我们在 《JavaScript深入之继承的多种方式和优缺点》 中介绍过。
引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:
ES6 extend
Class 通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
以上 ES5 的代码对应到 ES6 就是:
值得注意的是:
super 关键字表示父类的构造函数,相当于 ES5 的 Parent.call(this)。
子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。
也正是因为这个原因,在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。
子类的 __proto__
在 ES6 中,父类的静态方法,可以被子类继承。举个例子:
这是因为 Class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链。
(1)子类的 __proto__ 属性,表示构造函数的继承,总是指向父类。
(2)子类 prototype 属性的 __proto__ 属性,表示方法的继承,总是指向父类的 prototype 属性。
ES6 的原型链示意图为:
我们会发现,相比寄生组合式继承,ES6 的 class 多了一个
Object.setPrototypeOf(Child, Parent)
的步骤。继承目标
extends 关键字后面可以跟多种类型的值。
上面代码的 A,只要是一个有 prototype 属性的函数,就能被 B 继承。由于函数都有 prototype 属性(除了 Function.prototype 函数),因此 A 可以是任意函数。
除了函数之外,A 的值还可以是 null,当
extend null
的时候:Babel 编译
那 ES6 的这段代码:
Babel 又是如何编译的呢?我们可以在 Babel 官网的 Try it out 中尝试:
我们可以看到 Babel 创建了 _inherits 函数帮助实现继承,又创建了 _possibleConstructorReturn 函数帮助确定调用父类构造函数的返回值,我们来细致的看一看代码。
_inherits
关于 Object.create(),一般我们用的时候会传入一个参数,其实是支持传入两个参数的,第二个参数表示要添加到新创建对象的属性,注意这里是给新创建的对象即返回值添加属性,而不是在新创建对象的原型对象上添加。
举个例子:
再完整一点:
那么对于这段代码:
作用就是给 subClass.prototype 添加一个可配置可写不可枚举的 constructor 属性,该属性值为 subClass。
_possibleConstructorReturn
函数里是这样调用的:
我们简化为:
_possibleConstructorReturn
的源码为:在这里我们判断
Parent.call(this, name)
的返回值的类型,咦?这个值还能有很多类型?对于这样一个 class:
Parent.call(this, name) 的值肯定是 undefined。可是如果我们在 constructor 函数中 return 了呢?比如:
我们可以返回各种类型的值,甚至是 null:
我们接着看这个判断:
注意,这句话的意思并不是判断 call 是否存在,如果存在,就执行
(typeof call === "object" || typeof call === "function") ? call : self
因为
&&
的运算符优先级高于? :
,所以这句话的意思应该是:对于 Parent.call(this) 的值,如果是 object 类型或者是 function 类型,就返回 Parent.call(this),如果是 null 或者基本类型的值或者是 undefined,都会返回 self 也就是子类的 this。
这也是为什么这个函数被命名为
_possibleConstructorReturn
。总结
最后我们总体看下如何实现继承:
首先执行
_inherits(Child, Parent)
,建立 Child 和 Parent 的原型链关系,即Object.setPrototypeOf(Child.prototype, Parent.prototype)
和Object.setPrototypeOf(Child, Parent)
。然后调用
Parent.call(this, name)
,根据 Parent 构造函数的返回值类型确定子类构造函数 this 的初始值 _this。最终,根据子类构造函数,修改 _this 的值,然后返回该值。
ES6 系列
ES6 系列目录地址:https://github.com/mqyqingfeng/Blog
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: