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