在使用es6語法class的時候,babel到底做了什麼?
阿新 • • 發佈:2019-01-04
自從有了webpack之後,我們這些jscoder似乎得到了前所未有的解放,箭頭函式,物件解構,let,const關鍵字,以及class、extends等等關鍵字使用得不亦樂乎,反正,webpack會幫我們把這些es6程式碼轉換成瀏覽器能夠識別的es5程式碼,那麼,我們有多少人真正的看過,babel轉換之後的程式碼呢?今天,我就來看一下,當我們使用關鍵詞class的時候,babel到底做了什麼?
我推薦開啟網址:https://babeljs.io/repl,這裡我們左邊寫es6程式碼,馬上右邊就能轉譯出es5程式碼,然後,我在左邊輸入瞭如下程式碼:
class A { constructor(name) { this.name = name } getName() { return this.name } }
這是一個最簡單的類,一個屬性,一個方法。
這時候,右邊框已經給我轉譯出了瀏覽器可識別的es5程式碼了,格式化之後是這樣的:
'use strict'; var _createClass = function () { 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); } } 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 A = function () { function A(name) { _classCallCheck(this, A); this.name = name; } _createClass(A, [{ key: 'getName', value: function getName() { return this.name; } }]); return A; }();
好,現在來分析一下這段程式碼
2、es6裡面的類,本質上其實就是一個函式
// 自執行函式 var A = function () { function A(name) { // 這個函式的目的其實是防止這個建構函式被當做普通函式執行 _classCallCheck(this, A); this.name = name; } // 對函式A執行_createClass方法,其實就是給A的原型上繫結方法 _createClass(A, [{ key: 'getName', //方法名 value: function getName() { //函式體 return this.name; } }]); return A; }();
這段程式碼,變數A是一個自執行函式的返回值,該自執行函式的返回值其實就是我們熟悉的建構函式,所以,es6裡面的類其實就是一個建構函式。
3、_classCallCheck函式
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
這個函式特別簡單,當執行函式A的時候,不允許this不是A的子類例項,比如直接這樣呼叫A(),但是在A的子類B中可以這樣呼叫:A.apply(this, arguments)。該函式的目的是防止建構函式被當做普通函式執行。4、_createClass函式
//該函式也是一個自執行的函式,其返回值是一個函式var _createClass =function(){// 把props陣列上每一個物件,通過Object.defineProperty方法,都定義到目標物件target上去function defineProperties(target, props){for(var i =0; i < props.length; i++){//這裡要確保props[i]是一個物件,並且有key和value兩個鍵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);}}returnfunction(Constructor, protoProps, staticProps){// 如果傳入了原型屬性陣列,就把屬性全部定義到Constructor的原型上去if(protoProps) defineProperties(Constructor.prototype, protoProps);// 如果傳入了靜態屬性陣列,就把屬性全部定義到Constructor物件自身上去if(staticProps) defineProperties(Constructor, staticProps);returnConstructor;};}();
其實_createClass函式做的事情,就是把幾個方法拷貝到建構函式A的原型上去4、使用關鍵詞extends,發生了什麼?
class B extends A {}
這時候,右側多出了以下幾行程式碼: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: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var B = function (_A) {
_inherits(B, _A);
function B() {
_classCallCheck(this, B);
//這裡的重點是第二個引數:(B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments);
//這裡其實是將子類的例項物件,呼叫了父類的建構函式方法,這樣父類的屬性就都可以拷貝到子類上來
return _possibleConstructorReturn(this, (B.__proto__ || Object.getPrototypeOf(B)).apply(this, arguments));
}
return B;
}(A);
5、_inherits函式
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);
}
//把子類的原型指向父類的原型創建出來的物件(注意不是直接指向父類原型),並且修正constructor屬性為子類自己
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
// 這一步操作,其實是想把superClass放到subClass下,相當於subClass.super = superClass,這樣後面的程式碼中,subClass裡面能方便的引用到superClass函式
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
6、_possibleConstructorReturn函式
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;
}
如果call不是物件或者函式,即該呼叫:(B.proto || Object.getPrototypeOf(B)).apply(this, arguments)的返回值既不是物件,也不是函式,那麼,就直接返回當前的self,而self其實就是子類B裡面的例項指標this。正常情況,(B.proto || Object.getPrototypeOf(B)).apply(this, arguments)的返回值就是一個物件,其實也就是物件。
好了,上面算是基本說清楚了使用es6預發定義類、繼承類,到底發生了什麼,如果錯誤,還請指正,謝謝!
原文:https://segmentfault.com/a/1190000015125847