经常使用ES6中的class,但却一直不知道 babel 是如何编译的,所以就抽空研究了一下,下面是相关的代码,关键地方都已经添加了注释。
1. 编译类
ES5 定义类
function Person(name) {
this.name = name;
}
Person.prototype = {
sayHello: function () {
return 'hello, I am ' + this.name;
},
get name() {
return 'kevin';
},
set name(newName) {
console.log('new name 为:' + newName)
}
}
Person.onlySayHello = function () {
return 'hello'
};
ES6 定义类
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
return 'hello, I am ' + this.name;
}
static onlySayHello() {
return 'hello'
}
get name() {
return 'kevin';
}
set name(newName) {
console.log('new name 为:' + newName)
}
}
babel 转义后代码:
'use strict';
var _createClass = function () {
// 遍历传入的属性数组并添加到Person上
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
// Person,Person原型上方法和Person自身的方法
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Person = function () {
function Person(name) {
_classCallCheck(this, Person); // 判断是否new调用
this.name = name;
}
_createClass(Person, [{
key: 'sayHello',
value: function sayHello() {
return 'hello, I am ' + this.name;
}
}, {
key: 'name',
get: function get() {
return 'kevin';
},
set: function set(newName) {
console.log('new name 为:' + newName);
}
}], [{
key: 'onlySayHello',
value: function onlySayHello() {
return 'hello';
}
}]);
return Person;
}();
babel 将Person分解在了两个数组中,一个数组存储原型上的方法,一个数组储存静态方法,然后传入到_createClass中处理,在 _createClass中通过定义的defineProperties方法将传入的方法添加到构造函数Person上,可以看出,ES6中类和ES5上面最大的不同就是增加了静态方法的添加,所以通过ES6生成的实例可以直接调用父类的静态方法,而使用ES5方式生成的实例调用时则会报错。
2. 编译继承
那么babel又是如何编译继承的呢?
ES5 寄生组合式继承
function Parent (name) {
this.name = name;
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
var child1 = new Child('kevin', '18');
console.log(child1);
ES6 extend
class Parent {
constructor(name) {
this.name = name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
var child1 = new Child('kevin', '18');
console.log(child1);
babel 转义后代码:
'use strict';
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
// 继承父类原型方法
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { // 添加一个constructor属性指向子类原型
value: subClass,
enumerable: false, // 不可枚举
writable: true,
configurable: true
}
});
// 继承父类静态方法,可简化为: Child.__proto__ = Parent。
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Parent = function Parent(name) {
_classCallCheck(this, Parent);
this.name = name;
};
var Child = function (_Parent) {
_inherits(Child, _Parent);
function Child(name, age) {
_classCallCheck(this, Child);
// 将子类中this指向父类,可简化为 Parent.call(Child)
var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name));
_this.age = age;
return _this;
}
return Child;
}(Parent);
var child1 = new Child('kevin', '18');
console.log(child1);
关键步骤已经在代码中加了注释,总结一下,extend做了三件事情,
- 继承父类的原型方法和静态方法
- 改变子类原型的constructor指向
- 将子类中this指向父类,从而获取父类中的属性
完。
参考:
https://github.com/mqyqingfeng/Blog/issues/105