-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in 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: