摘要:年,很多人已經開始接觸環境,并且早已經用在了生產當中。我們發現,關鍵字會被編譯成構造函數,于是我們便可以通過來實現實例的生成。下一篇文章我會繼續介紹如何處理子類的并會通過一段函數橋梁,使得環境下也能夠繼承定義的。
2017年,很多人已經開始接觸ES6環境,并且早已經用在了生產當中。我們知道ES6在大部分瀏覽器還是跑不通的,因此我們使用了偉大的Babel來進行編譯。很多人可能沒有關心過,經過Babel編譯之后,我們華麗的ES6代碼究竟變成了什么樣子?
這篇文章,針對Babel對ES6里面“類class”的編譯進行分析,你可以在線測試編譯結果,畢竟紙上得來終覺淺,自己動手,才能真正體會其中的奧秘。
另外,如果你還不明白JS中原型鏈等OOP相關知識,建議出門左轉找到經典的《JS高級程序設計》來補課;如果你對JS中,通過原型鏈來實現繼承一直云里霧里,安利一下我的同事,前端著名網紅顏海鏡大大早在2014年的文章
為什么使用選擇BabelBabel:The compiler for writing next generation JavaScript;
我們知道,現在大部分瀏覽器或者類似NodeJS的javascript引擎還不能直接支持ES6語法。但這并不構成障礙,比如Babel的出現,使得我們在生產環境中書寫ES6代碼成為了現實,它工作原理是編譯ES6的新特性為老版本的ES5,從而得到宿主環境的支持。
在這篇文章中,我會講解Babel如何處理ES6新特性:Class,這其實是一系列語法糖的實現。
Old school方式實現繼承在探究ES6之前,我們先來回顧一下ES5環境下,我們如何實現類的繼承:
// Person是一個構造器 function Person(name) { this.type = "Person"; this.name = name; } // 我們可以通過prototype的方式來加一條實例方法 Person.prototype.hello = function() { console.log("hello " + this.name); } // 對于私有屬性(Static method),我們當然不能放在原型鏈上了。我們可以直接放在構造函數上面 Person.fn = function() { console.log("static"); };
我們可以這么應用:
var julien = new Person("julien"); var darul = new Person("darul"); julien.hello(); // "hello julien" darul.hello(); // "hello darul" Person.fn(); // "static" // 這樣會報錯,因為fn是一個私有屬性 julien.fn(); //Uncaught TypeError: julien.fn is not a functionNew school方式(ES6)實現繼承
在ES6環境下,我們當然迫不及待地試一試Class:
class Person { constructor(name) { this.name = name; this.type="person" } hello() { console.log("hello " + this.name); } static fn() { console.log("static"); }; }
這樣寫起來當然很cool,但是經過Babel編譯,我們的代碼是什么樣呢?
Babel transformation我們一步一步來看,
Step1: 定義
我們從最簡單開始,試試不加任何方法和屬性的情況下,
Class Person{}
被編譯為:
function _classCallCheck(instance, Constructor) { // 檢查是否成功創建了一個對象 if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person() { _classCallCheck(this, Person); };
你可能會一頭霧水,_classCallCheck是什么?其實很簡單,它是為了保證調用的安全性。
比如我們這么調用:
// ok var p = new Person();
是沒有問題的,但是直接調用:
// Uncaught TypeError: Cannot call a class as a function Person();
就會報錯,這就是_classCallCheck所起的作用。具體原理自己看代碼就好了,很好理解。
我們發現,Class關鍵字會被編譯成構造函數,于是我們便可以通過new來實現實例的生成。
Step2:Constructor探秘
我們這次嘗試加入constructor,再來看看編譯結果:
class Person() { constructor(name) { this.name = name; this.type = "person" } }
編譯結果:
var Person = function Person(name) { _classCallCheck(this, Person); this.type = "person"; this.name = name; };
看上去棒極了,我們繼續探索。
Step3:增加方法
我們嘗試給Person類添加一個方法:hello:
class Person { constructor(name) { this.name = name; this.type = "person" } hello() { console.log("hello " + this.name); } }
編譯結果(已做適當省略):
// 如上,已經解釋過 function _classCallCheck.... // MAIN FUNCTION 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; }; })(); var Person = (function () { function Person(name) { _classCallCheck(this, Person); this.name = name; } _createClass(Person, [{ key: "hello", value: function hello() { console.log("hello " + this.name); } }]); return Person; })();
Oh...no,看上去有很多需要消化!不要急,我嘗試先把他精簡一下,并加上注釋,你就會明白核心思路:
var _createClass = (function () { function defineProperties(target, props) { // 對于每一個定義的屬性props,都要完全拷貝它的descriptor,并擴展到target上 } return defineProperties(Constructor.prototype, protoProps); })(); var Person = (function () { function Person(name) { // 同之前... } _createClass(Person, [{ key: "hello", value: function hello() { console.log("hello " + this.name); } }]); return Person; })();
如果你不明白defineProperty方法, 請參考這里
現在,我們知道我們添加的方法:
hello() { console.log("hello " + this.name); }
被編譯為:
_createClass( Person, [{ key: "hello", value: function hello() { console.log("hello " + this.name); } }]);
而_createClass接受2個-3個參數,分別表示:
參數1 => 我們要擴展屬性的目標對象,這里其實就是我們的Person 參數2 => 需要在目標對象原型鏈上添加的屬性,這是一個數組 參數3 => 需要在目標對象上添加的屬性,這是一個數組
這樣,Babel的魔法就一步一步被揭穿了。
總結希望這篇文章能夠讓你了解到Babel是如何初步把我們ES6 Class語法編譯成ES5的。下一篇文章我會繼續介紹Babel如何處理子類的Super(), 并會通過一段函數橋梁,使得ES5環境下也能夠繼承ES6定義的Class。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81192.html
摘要:并且用驗證了中一系列的實質就是魔法糖的本質。抽絲剝繭我們首先看的編譯結果這是一個自執行函數,它接受一個參數就是他要繼承的父類,返回一個構造函數。 如果你已經看過第一篇揭秘babel的魔法之class魔法處理,這篇將會是一個延伸;如果你還沒看過,并且也不想現在就去讀一下,單獨看這篇也沒有關系,并不存在理解上的障礙。 上一篇針對Babel對ES6里面基礎class的編譯進行了分析。這一篇將...
摘要:我們都知道您是國內知名的專家,是什么樣的情結使得您愿意將魔法作為自己的別名大家好,很榮幸接受圖靈的專訪。在這一堆書里,有一套上下冊教程叫作談是由圖靈引進的哦。從偶像那里得來一個名字,很榮幸而且這其中也有圖靈的功勞,也是緣份。 非商業轉載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/216538 showImg(https:...
摘要:我們都知道您是國內知名的專家,是什么樣的情結使得您愿意將魔法作為自己的別名大家好,很榮幸接受圖靈的專訪。在這一堆書里,有一套上下冊教程叫作談是由圖靈引進的哦。從偶像那里得來一個名字,很榮幸而且這其中也有圖靈的功勞,也是緣份。 非商業轉載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/216538 showImg(https:...
摘要:它并不是實際在內部的工作方式,而且它只是一個提案,在未來都會有可能發生變化。這意味著,數據的存儲是獨立于組件之外的。因此,有一個訣竅就是你需要思考作為一組需要一個匹配一致的指針去管理的數組染陌譯。 原文地址:https://medium.com/@ryardley/... 譯文:染陌 (Github) 譯文地址:https://github.com/answershuto/Blog 轉...
閱讀 2772·2021-10-14 09:42
閱讀 827·2021-10-11 10:57
閱讀 773·2019-08-30 15:54
閱讀 1914·2019-08-30 13:50
閱讀 1686·2019-08-30 11:19
閱讀 932·2019-08-29 12:38
閱讀 1425·2019-08-26 11:51
閱讀 1388·2019-08-26 10:48