What is Babel?

Zero_A發表於2018-07-01

這篇文章是 Babel 6.x 官方文件 的部分內容翻譯,結合自己的理解,旨在梳理清楚關於 Babel 是什麼、如何執行、什麼是外掛和預設,以及如何在實際開發中使用等一系列的相關問題。

ECMAScript 版本介紹

阮一峰的《ECMAScript 6 入門》一書中對 JavaScript 版本演進過程進行了詳細介紹,以下是對其中部分內容的摘抄,原文在此 ECMAScript 6 簡介

ECMAScript 和 JavaScript 的關係

一個常見的問題是,ECMAScript 和 JavaScript 到底是什麼關係?

要講清楚這個問題,需要回顧歷史。1996 年 11 月,JavaScript 的創造者 Netscape 公司,決定將 JavaScript 提交給標準化組織 ECMA,希望這種語言能夠成為國際標準。次年,ECMA 釋出 262 號標準檔案(ECMA-262)的第一版,規定了瀏覽器指令碼語言的標準,並將這種語言稱為 ECMAScript,這個版本就是 1.0 版。

該標準從一開始就是針對 JavaScript 語言制定的,但是之所以不叫 JavaScript,有兩個原因。一是商標,Java 是 Sun 公司的商標,根據授權協議,只有 Netscape 公司可以合法地使用 JavaScript 這個名字,且 JavaScript 本身也已經被 Netscape 公司註冊為商標。二是想體現這門語言的制定者是 ECMA,不是 Netscape,這樣有利於保證這門語言的開放性和中立性。

因此,ECMAScript 和 JavaScript 的關係是,前者是後者的規格,後者是前者的一種實現(另外的 ECMAScript 方言還有 Jscript 和 ActionScript)。日常場合,這兩個詞是可以互換的。

ES6 與 ECMAScript 2015 的關係

ECMAScript 2015(簡稱 ES2015)這個詞,也是經常可以看到的。它與 ES6 是什麼關係呢?

2011 年,ECMAScript 5.1 版釋出後,就開始制定 6.0 版了。因此,ES6 這個詞的原意,就是指 JavaScript 語言的下一個版本。

但是,因為這個版本引入的語法功能太多,而且制定過程當中,還有很多組織和個人不斷提交新功能。事情很快就變得清楚了,不可能在一個版本里麵包括所有將要引入的功能。常規的做法是先發布 6.0 版,過一段時間再發 6.1 版,然後是 6.2 版、6.3 版等等。

但是,標準的制定者不想這樣做。他們想讓標準的升級成為常規流程:任何人在任何時候,都可以向標準委員會提交新語法的提案,然後標準委員會每個月開一次會,評估這些提案是否可以接受,需要哪些改進。如果經過多次會議以後,一個提案足夠成熟了,就可以正式進入標準了。這就是說,標準的版本升級成為了一個不斷滾動的流程,每個月都會有變動。

標準委員會最終決定,標準在每年的 6 月份正式釋出一次,作為當年的正式版本。接下來的時間,就在這個版本的基礎上做改動,直到下一年的 6 月份,草案就自然變成了新一年的版本。這樣一來,就不需要以前的版本號了,只要用年份標記就可以了。

ES6 的第一個版本,就這樣在 2015 年 6 月釋出了,正式名稱就是《ECMAScript 2015 標準》(簡稱 ES2015)。2016 年 6 月,小幅修訂的《ECMAScript 2016 標準》(簡稱 ES2016)如期釋出,這個版本可以看作是 ES6.1 版,因為兩者的差異非常小(只新增了陣列例項的includes方法和指數運算子),基本上是同一個標準。根據計劃,2017 年 6 月釋出 ES2017 標準。

因此,ES6 既是一個歷史名詞,也是一個泛指,含義是 5.1 版以後的 JavaScript 的下一代標準,涵蓋了 ES2015、ES2016、ES2017 等等,而 ES2015 則是正式名稱,特指該年釋出的正式版本的語言標準。本書中提到 ES6 的地方,一般是指 ES2015 標準,但有時也是泛指“下一代 JavaScript 語言”。

語法提案的批准流程

任何人都可以向標準委員會(又稱 TC39 委員會)提案,要求修改語言標準。

一種新的語法從提案到變成正式標準,需要經歷五個階段。每個階段的變動都需要由 TC39 委員會批准。

  • Stage 0 - Strawman(展示階段)
  • Stage 1 - Proposal(徵求意見階段)
  • Stage 2 - Draft(草案階段)
  • Stage 3 - Candidate(候選人階段)
  • Stage 4 - Finished(定案階段)

一個提案只要能進入 Stage 2,就差不多肯定會包括在以後的正式標準裡面。ECMAScript 當前的所有提案,可以在 TC39 的官方網站Github.com/tc39/ecma26…檢視。

What is Babel?

Babel 是一個 JavaScript 編譯器(compiler)。

主要用於在舊版瀏覽器或環境中將ECMAScript 2015+程式碼轉換為向後相容的JavaScript版本。

由ECMAScript 版本演進的介紹可知,JavaScript 語言的標準規範在每一年都會有所更新,在每個版本定版之前還會經歷從提案到定案的不同階段,不同的執行時(瀏覽器和 Node 等)、相同執行時的不同版本之間,對新的語言規範的實現情況都會不同,因此在使用新的語法特性的時候就顯得狼狽不堪。Babel 就是為了解決這一問題,運用 Babel 編譯器能夠使得開發人員在寫程式碼的時候完全不需要考慮執行時對新語法特性的支援程度,因為最終我們所寫的ECMAScript 2015+的程式碼都會被 Babel轉換為向後相容(ECMAScript 2015或ECMAScript 2015之前版本)的程式碼。

外掛

Babel是一個編譯器。編譯程式碼的過程分為3個階段:解析(parsing)、轉換(transforming)、生成(generation)。

解析時,Babel 並不執行任何操作,就像const babel = code => code一樣,通過解析程式碼然後再次生成相同的程式碼。你需要為Babel新增一些外掛來做一些事情(外掛影響第二階段的轉換過程)。

對於 Babel 的編譯過程,解析是基礎,所有的語法轉換的工作全部由外掛完成,不同的外掛負責不同的語法轉換,比如plugin-transform-es2015-arrow-functions這個外掛就只負責對箭頭函式語法的轉換。因此, Babel 對於語法規範的不同版本的不同特性都是由這樣的一個個外掛完成的。

將一堆外掛組合在一起,便構成了預設(preset)。官方給出的外掛組合及預設有:

What is Babel?

其中預設babel-preset-env等同於babel-preset-latestbabel-preset-es2015 + babel-preset-es2016 + babel-preset-es2017 和 ,對於一些老的專案可能還在使用babel-preset-es2015,現在可以考慮使用babel-preset-env進行替換。因此,現在最常見的預設配置為env + stage-3

{
  "presets": [ "env","stage-3" ]
}

複製程式碼

許多其他社群維護的預設可在 npm 上找到。

Polyfill

Babel 作為編譯器,只對語法部分做轉換,如:

[1,2,3].map(n => n + 1); 
複製程式碼

轉換為:

[1,2,3].map(function (n) {
    return n + 1;
}
複製程式碼

上述程式碼只轉換了箭頭函式語法,但對陣列的新原生方法 map 卻並未做任何改變。

為了支援新的原生物件和方法,需要使用babel-polyfill。像 plugins 一樣,也可以或選擇性地包含所需要的內容。

注意事項

由於ES5的限制,內建類(如Date,Array,DOM等)無法正確進行子類繼承。

ES5

由於Babel假定程式碼將在ES5環境中執行,因此它會使用ES5函式。如果你當前使用的環境對ES5的支援有限或根本不支援(例如較低版本的IE),那麼使用babel-polyfill將會增加對這些方法的支援。

IE

類 (10 and below)

如果你的類繼承自另外一個類,那麼靜態屬性是通過__proto__繼承的,這是最基本的認知,但是在10及10以下的 IE可能會遇到很多的問題。因為IE <= 10不支援__proto__,因此靜態屬性不會被繼承。

Getters/Setters(8 and below)

IE8及8以下版本的 IE 不支援getters和setters,而且在IE8中,Object.defineProperty 只能用於DOM物件。

因此,如果你的程式碼計劃支援IE8或更低版本,則不建議使用getter和setter,因為用了也沒用。