紀錄工作經驗、相關知識,解決技術相關問題。

Javascript, 新手 Jamp Holes

ECMAScript 是什麼?和 JavaScript 的關係、ES6 與年度版本怎麼看

ECMAScript 是什麼?和 JavaScript 的關係、ES6 與年度版本怎麼看 特色圖片

ECMAScript 是什麼? ECMAScript 是 Ecma International 在 ECMA-262 標準裡定義的程式語言規格,JavaScript 是依照這份規格實作出來的語言。常聽到的「ES6」「ES2015」「ES2022」都是這份規格的版本名稱:規格每年出一版,新語法與新方法就是這樣一年一年加進 JavaScript。本文說明 ECMAScript 與 JavaScript、DOM 各自負責什麼,ES6 和年度版本怎麼對應,並用 Node.js 與 Chrome 跑出不同年度加入的語法,最後整理新語法進入規格的 TC39 提案流程。

ECMAScript 是什麼:JavaScript 的標準規格

ECMAScript 規格的 Introduction 寫明,ECMAScript 以數項起源技術為基礎,最知名的是 Netscape 的 JavaScript 與 Microsoft 的 JScript。這個語言由 Netscape 的 Brendan Eich 發明,最早出現在 Navigator 2.0 瀏覽器,規格第一版於 1997 年 6 月由 Ecma 大會通過。

MDN 對兩者關係的說法是:JavaScript 由 Ecma International 標準化,標準化後的版本稱為 ECMAScript,文件是 ECMA-262,所有支援這個標準的應用程式行為都一致。所以日常可以這樣分:

名稱是什麼誰負責
ECMAScript語言規格:語法、型別、Array、Promise 等內建物件Ecma International 的 TC39 委員會,文件是 ECMA-262
JavaScript依規格實作的語言,平常說的「寫 JavaScript」各家引擎與執行環境
DOM操作網頁文件的介面,例如 documentW3C、WHATWG

MDN 明確寫到,ECMAScript 規格並不描述 DOM。同時它也提醒,規格是給實作引擎(例如 Firefox 的 SpiderMonkey、Chrome 的 V8)的人看的,不是寫給一般寫程式的人,寫程式請看 JavaScript 文件。

規格只管語言,物件由宿主環境提供

ECMA-262 的 Overview 說,規格定義的 ECMAScript 並不打算自給自足:規格裡沒有輸入外部資料或輸出運算結果的機制。這些東西由宿主環境(host environment)補上,規格舉的例子是網頁瀏覽器,會提供視窗、對話框、cookie 等物件。

把同一段程式分別丟給 Node.js 與瀏覽器就看得出來。globalThis 與 setTimeout 兩邊都有,window、document 只有瀏覽器有,process、require 只有 Node.js 有:

console.log('typeof globalThis =', typeof globalThis);
console.log('typeof window     =', typeof window);
console.log('typeof document   =', typeof document);
console.log('typeof process    =', typeof process);
console.log('typeof require    =', typeof require);
console.log('typeof setTimeout =', typeof setTimeout);
Node.js v22.20.0 與 Chrome 158 headless、Windows 11 執行同一段 typeof 檢查的輸出

圖中上半是 Node.js v22.20.0,下半是 Chrome 158.0.8127.18(headless)的輸出,兩邊都是同一種 JavaScript 語法,差別在宿主環境提供的物件。document 這類瀏覽器物件的操作可另外學;這裡只要記得,在 Node.js 執行 document.title 會得到 ReferenceError: document is not defined,原因是宿主環境沒有這個物件,不是語法錯誤。

ES6、ES2015、第 6 版:版本名稱怎麼對應

ECMA-262 的版本有兩種叫法,「第幾版」與「ES+年份」,指的是同一份規格:

  • 第 6 版的標題是 ECMAScript 2015 Language Specification,2015 年 6 月通過。ES6 就是這一版,也寫成 ES2015。
  • 規格 Introduction 寫明,ECMAScript 2016 是第一個採用 TC39 新的「每年發行」節奏與開放開發流程的版本。所以第 6 版之後,每年 6 月左右出一版:第 7 版是 2016,第 8 版是 2017,依此類推。
  • 第 6 版以前是用編號:第 1 版 1997 年 6 月、第 2 版 1998 年 8 月、第 3 版 1999 年 12 月、第 5 版 2009 年 12 月、第 5.1 版 2011 年 6 月。第 4 版沒有發行,Ecma 頁面特別註明「ECMA-262 Edition 4」這個編號保留了但從未使用。

Ecma 官方頁面目前列出的最新版是 ECMAScript 2026,第 17 版,2026 年 6 月。tc39.es/ecma262 上的則是持續更新的草稿:規格寫明它包含最近一次年度快照,加上已達 Stage 4 的提案,這些會進入下一次的年度快照。本文撰寫時這份草稿的標題是 ECMAScript 2027。

第 6 版是變動最大的一版

第 6 版的 Introduction 寫得很清楚:這是持續了十五年的努力成果,主要新增包含模組、class 宣告、區塊作用域、迭代器與產生器、Promise、解構等。規格同時寫明,第 6 版為之後固定、漸進式的語言與函式庫增強奠定了基礎,也就是後面年度版本的起點。

年度版本各加了什麼

規格 Introduction 對前幾個年度版本的描述如下:ES2016 新增指數運算子(**)與 Array.prototype.includes;ES2017 新增 async function,以及 Object.values、Object.entries。再往後,常見語法與方法所屬的版本如下(版本以 Ecma 規格頁面核對,年份以 TC39 finished-proposals 為準):

語法或方法規格版本範例
指數運算子、Array.prototype.includesES2016(第 7 版)2 ** 10、[1, 2].includes(2)
async functionES2017(第 8 版)async function f() {}
可選串連 ?.、空值合併 ??ES2020(第 11 版)user.profile?.name ?? '訪客'
Array.prototype.atES2022(第 13 版)[10, 20, 30].at(-1)
Object.groupByES2024(第 15 版)Object.groupBy(items, fn)

核對方式是對照各版本的規格頁面:第 13 版(2022)有 Array.prototype.at 的條目,第 12 版(2021)沒有;第 15 版(2024)有 Object.groupBy,第 14 版(2023)沒有;第 11 版(2020)的文法有 OptionalExpression 與 CoalesceExpression,第 10 版(2019)沒有。

跑一次各年度語法

下面這段程式依序用到上表的語法:

console.log('ES2016  2 ** 10              =', 2 ** 10);
console.log('ES2016  includes(2)          =', [1, 2, 3].includes(2));

async function hello() {
  return 'async ok';
}
hello().then((v) => console.log('ES2017  async function       =', v));

const user = { profile: null };
console.log('ES2020  user.profile?.name   =', user.profile?.name);
console.log('ES2020  ?? 預設值            =', user.profile?.name ?? '訪客');
console.log('ES2022  [10, 20, 30].at(-1)  =', [10, 20, 30].at(-1));
console.log(
  'ES2024  Object.groupBy       =',
  JSON.stringify(Object.groupBy([1, 2, 3, 4], (n) => (n % 2 ? 'odd' : 'even')))
);
Node.js v22.20.0 與 Chrome 158 headless、Windows 11 執行各年度語法的輸出

圖中兩個環境的輸出一致:2 ** 10 是 1024,user.profile?.name 遇到 null 時不報錯而回傳 undefined,?? 補上預設值「訪客」,at(-1) 取到最後一個元素 30,Object.groupBy 把陣列依奇偶分成 odd、even 兩組。async function 的結果因為是 Promise,排在同步輸出之後才印出。

這也說明了「新語法能不能用」要看執行環境的版本:同樣的語法,在版本較舊的環境可能不被支援。Node.js 官方文件說,Node.js 以現代版 V8 建置,跟進 V8 讓 ECMA-262 的新功能能在 Node.js 使用;V8 認為穩定的功能預設開啟、不需要任何旗標。要看各個 Node.js 版本支援哪些 ECMAScript 功能,官方文件指向 node.green;查目前 Node.js 內建的 V8 版本可執行 node -p process.versions.v8。

數字與日期的格式化(例如千分位)不在 ECMA-262,而是另一份 ECMA-402 國際化 API 規格;ECMA-262 的參考規範明列它,Intl.NumberFormat 就在 ECMA-402。用法可前往〈JS 數字加逗號 / 轉換千分位顯示(toLocaleString 與正規表達式)〉觀看。

新語法怎麼進規格:TC39 提案流程

誰決定哪些語法能進 ECMAScript?是 Ecma 的 TC39 委員會,流程記載在 tc39.es/process-document。官方說明共有六個階段:一個 strawperson 階段加五個成熟度階段,每個階段的推進都要委員會核准。

Stage官方說明的狀態
0新提案,委員會目前沒有在考慮
1委員會正在考慮,找出問題範圍與可能的解法
2委員會選定偏好的解法,但設計還是草稿,可能大幅改動
2.7原則上通過,進入驗證階段,之後主要依測試、實作與使用回饋調整
3建議開始實作,預期不會有變動,但仍可能因網頁相容性或實作回饋做必要調整
4功能完整,準備納入標準,不再變動

進入 Stage 4 的條件包含:兩個相容的實作通過 Test262 驗收測試、已有實際出貨實作的使用經驗,以及已對規格倉庫送出整合規格文字的 pull request。Stage 4 的提案會整理在 tc39/proposals 的 finished-proposals 清單,並寫明預期發布的年份;前面表格的年份就是查這份清單。

規格的年度節奏,TC39 流程文件寫的是:預計每年 7 月把規格送交 Ecma 大會批准;3 月的會議把 Stage 4 提案納入並從主線切出新版本,之後只接受編輯性修改。所以一個語法什麼時候「能用」要分兩件事看:進入 Stage 4 代表已有實作可用,而它屬於哪個年度版本,看 finished-proposals 的預期年份與當年度規格。

寫程式時哪裡會碰到 ECMAScript 版本

最常碰到的地方是工具設定。例如 ESLint 的 languageOptions.ecmaVersion,官方文件說預設是 "latest",表示被檢查的程式碼所用的 ECMAScript 版本,同時決定可用的語法與全域變數;可以設成 3 或 5,或 2015 到現在之間的任一年份,官方建議多數情況用預設的 "latest"。ESLint 的基本設定可前往〈ESLint 是什麼?JavaScript 程式碼檢查工具的用途、規則與設定檔入門〉。

另一種是看到「不是標準」的語法。JSX 就是例子:規格寫明 JSX 是 ECMAScript 的類 XML 語法擴充,沒有定義語意,也不打算讓瀏覽器或引擎直接實作,要由工具轉成標準 ECMAScript,細節可前往〈JSX 是什麼?React JSX 語法規則、與 HTML 的差異和轉譯原理〉。碰到 <h1> 這種寫在 JavaScript 裡的標記時,可以用本文的分法判斷:它不在 ECMAScript 裡。

整理成幾個判斷原則:

  • 語法或內建物件(Array、Promise、?.)屬於 ECMAScript,行為看引擎版本。
  • window、document 屬於瀏覽器宿主環境與 DOM 規格,process、require 屬於 Node.js,都不在 ECMAScript 裡。
  • 看到「ES6」「ES2015」是同一個版本,是 2015 年 6 月通過的第 6 版;年份版本從 ES2016 開始每年一版。
  • 想知道新語法能不能用:先到 finished-proposals 查它屬於哪個年度,再查執行環境(例如 Node.js 查 node.green)支援與否。

參考資料與延伸閱讀

發佈留言