3. 一個 JavaScript:避免在 ECMAScript 6 中進行版本控制
目錄
請支持這本書:購買它(PDF、EPUB、MOBI)捐款
(廣告,請不要封鎖。)

3. 一個 JavaScript:避免在 ECMAScript 6 中進行版本控制

為語言新增新功能的最佳方式是什麼?本章節說明 ECMAScript 6 採取的方法。它被稱為「一個 JavaScript」,因為它避免了版本控制。



3.1 版本控制

原則上,語言的新版本是一個清理它的機會,方法是移除過時的特色或變更特色運作的方式。這表示新的程式碼無法在較舊的語言實作中運作,而舊的程式碼也無法在新實作中運作。每一小段程式碼都連結到語言的特定版本。處理不同版本時,有兩種常見的方法。

首先,你可以採取「全有或全無」的方法,並要求如果程式碼庫想要使用新版本,就必須完全升級。Python 在從 Python 2 升級到 Python 3 時採取了這種方法。它的問題在於,一次將所有現有的程式碼庫進行移轉可能不可行,特別是當它很龐大的時候。此外,這種方法對網路來說並非選項,因為你永遠都會有舊程式碼,而且 JavaScript 引擎會自動更新。

其次,你可以允許程式碼庫包含多個版本的程式碼,方法是為程式碼加上版本標籤。在網路上,你可以透過專用的網際網路媒體類型標記 ECMAScript 6 程式碼。此類媒體類型可以透過 HTTP 標頭與檔案關聯

Content-Type: application/ecmascript;version=6

也可以透過 <script> 元素的 type 屬性關聯(其預設值text/javascript

<script type="application/ecmascript;version=6">
    ···
</script>

這指定版本超出頻段,在實際內容外部。另一個選項是在內容內指定版本(頻段內)。例如,透過以下列開始檔案

use version 6;

兩種標記方式都有問題:頻段外版本易碎且容易遺失,頻段內版本會為程式碼增加雜訊。

更根本的問題是,允許每個程式碼庫有多個版本,實際上會將語言分叉成必須並行維護的子語言。這會造成問題

因此,版本控制是需要避免的事情,特別是對於 JavaScript 和網路。

3.1.1 沒有版本控制的演進

但我們如何擺脫版本控制?透過永遠保持向下相容性。這表示我們必須放棄一些我們對清理 JavaScript 的野心:我們不能引入重大變更。保持向下相容性表示不移除功能,也不變更功能。此原則的口號是:「不要破壞網路」。

不過,我們可以新增新功能,並讓現有功能更強大。

因此,新引擎不需要版本,因為它們仍然可以執行所有舊程式碼。David Herman 將這種避免版本控制的方法稱為 One JavaScript (1JS) [1],因為它避免將 JavaScript 分割成不同的版本或模式。正如我們稍後將看到的,1JS 甚至會取消一些已經存在的分割,因為嚴格模式。

One JavaScript 並不表示您必須完全放棄清理語言。您不是清理現有功能,而是引入新的、乾淨的功能。一個範例是 let,它宣告區塊範圍變數,而且是 var 的進階版本。不過,它並未取代 var。它與 var 並存,作為較佳的選項。

有一天,甚至有可能移除不再有人使用的功能。ES6 的一些功能是透過調查網路上的 JavaScript 程式碼而設計的。兩個範例是

3.2 嚴格模式和 ECMAScript 6

嚴格模式 在 ECMAScript 5 中引入,用於清理語言。它會在檔案或函式中將下列列第一行開啟

'use strict';

嚴格模式引入了三種中斷變更

嚴格模式是一個很好的例子,說明了版本控制為何棘手:儘管它啟用了更簡潔的 JavaScript 版本,但它的採用率仍然相對較低。主要原因是它會中斷一些現有程式碼,可能會減慢執行速度,而且將其新增到檔案(更不用說互動式命令列)很麻煩。我喜歡嚴格模式這個想法,但卻沒有經常使用它。

3.2.1 支援隨意(非嚴格)模式

一個 JavaScript 表示我們無法放棄隨意模式:它將繼續存在(例如在 HTML 屬性中)。因此,我們無法在嚴格模式之上建構 ECMAScript 6,我們必須將其功能新增到嚴格模式和非嚴格模式(又稱隨意模式)中。否則,嚴格模式將會是語言的不同版本,我們將回到版本控制。不幸的是,兩個 ECMAScript 6 功能難以新增到隨意模式:let 宣告和區塊層級函式宣告。讓我們來探討原因以及如何新增它們。

3.2.2 隨意模式中的let 宣告

let 讓您可以宣告區塊範圍變數。難以新增到隨意模式,因為 let 僅在嚴格模式中為保留字。也就是說,以下兩個陳述式為合法的 ES5 隨意程式碼

var let = [];
let[x] = 'abc';

在嚴格 ECMAScript 6 中,您會在第 1 行收到例外,因為您將保留字 let 用作變數名稱。而第 2 行中的陳述式會被解釋為 let 變數宣告(使用解構)。

在隨意 ECMAScript 6 中,第一行不會導致例外,但第二行仍會被解釋為 let 宣告。在網路上使用識別碼 let 的這種方式非常罕見,因此 ES6 可以負擔得起這種解釋。撰寫 let 宣告的其他方式不會被誤認為隨意 ES5 語法

let foo = 123;
let {x,y} = computeCoordinates();

3.2.3 隨意模式中的區塊層級函式宣告

ECMAScript 5 嚴格模式禁止在區塊中宣告函式。規範在草率模式中允許宣告函式,但未指定函式應如何運作。因此,各個 JavaScript 實作支援函式宣告,但處理方式不同。

ECMAScript 6 希望區塊中的函式宣告僅限於該區塊。這作為 ES5 嚴格模式的延伸是可以的,但會中斷一些草率的程式碼。因此,ES6 為瀏覽器提供「網頁舊版相容性語意」,讓區塊中的函式宣告存在於函式範圍中。

3.2.4 其他關鍵字

識別碼 yieldstatic 僅在 ES5 嚴格模式中保留。ECMAScript 6 使用特定於內容的語法規則,讓這些識別碼在草率模式中運作

3.2.5 隱含嚴格模式

模組和類別的主體在 ECMAScript 6 中會隱含嚴格模式,不需要 'use strict' 標記。考量到我們的程式碼在未來幾乎都會存在於模組中,ECMAScript 6 實際上將整個語言提升為嚴格模式。

其他建構的主體(例如箭頭函式和產生器函式)也可以隱含嚴格。但考量到這些建構通常很小,在草率模式中使用它們會導致程式碼在兩種模式之間分散。類別和模組特別大,因此分散會比較不是問題。

3.2.6 無法修正的事項

One JavaScript 的缺點是無法修正現有的怪癖,特別是以下兩個。

首先,typeof null 應傳回字串 'null',而非 'object'。TC39 嘗試修正它,但它中斷了現有的程式碼。另一方面,新增新類別運算元的結果是沒問題的,因為目前的 JavaScript 引擎偶爾會為宿主物件傳回自訂值。ECMAScript 6 的符號就是一個例子

> typeof Symbol.iterator
'symbol'

其次,全域物件 (瀏覽器中的 window) 不應在變數的範圍鏈中。但現在要變更它也為時已晚。至少,在模組中不會在全域範圍內,而且 let 永遠不會建立全域物件的屬性,即使在全域範圍內使用也是如此。

3.3 ES6 中的重大變更

ECMAScript 6 確實引入了幾個次要的重大變更 (您不太可能會遇到)。它們列在兩個附錄中

3.4 結論

One JavaScript 表示讓 ECMAScript 6 完全向後相容。成功做到這一點真是太棒了。特別感謝的是,模組 (因此我們的大部分程式碼) 隱含地在嚴格模式中。

短期而言,將 ES6 結構新增到嚴格模式和隨意模式中,在撰寫語言規格和在引擎中實作時,會增加更多工作。長期而言,規格和引擎都受益於語言沒有分岔 (減少膨脹等)。程式設計師立即從 One JavaScript 中受益,因為它讓入門 ECMAScript 6 變得更容易。

3.5 進一步閱讀

[1] 原始的 1JS 提議 (警告:已過時):「ES6 不需要選擇加入」,作者為 David Herman。

下一篇:4. ES6 核心功能