經常使用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指向父類,從而獲取父類中的屬性
完。