Skip to content

2026-06-22

/ 12 分鐘閱讀

/ Deep JavaScript Foundations v3

ES6 模組語法:原生模組支援與 Node.js 的相容之路 ​

本篇延續上一篇的模組模式討論。上一篇說明了用 IIFE 和工廠函式手工實作模組的方式。本篇進入 JavaScript 語言層級的原生支援:ES6 模組語法,並探討它在實際採用上的歷史障礙。

ES6 模組:期待已久的語言原生支援 ​

多年來,社群一直呼籲 JavaScript 應該為這麼重要的設計模式提供第一等的語法支援。ES6 終於帶來了原生模組語法,但它的普及之路並不順遂。

ES6 模組與 Node.js 的相容問題 ​

ES6 模組規格推出後,TC39 與 Node.js 社群之間出現了一段嚴重的溝通落差:兩方在設計階段並未充分協調,導致新規格與 Node.js 長期使用的 CommonJS(require/module.exports)系統存在根本性的不相容。

問題的核心之一是循環引用(circular dependency):當一個 CommonJS 模組 require 了一個 ES6 模組,而那個 ES6 模組又 import 了原來的 CommonJS 模組時,兩套系統的載入行為會產生難以預測的結果。

Node.js 採用的妥協方案之一,是要求使用 .mjs 副檔名來標示 ES6 模組,以便與 .js 的 CommonJS 模組區分。這個妥協對許多人來說代價過高。Kyle Simpson 在錄製本課程時(約 2019 年)選擇繼續使用經典模組模式,原因正是不想在這場規格動盪尚未落定之前貿然採用新語法。

ES6 模組的語法與特性 ​

儘管有上述背景,ES6 模組本身的設計有幾個清楚的特性值得理解。

預設私有、明確公開 ​

開啟一個 ES6 模組檔案後,裡面的所有變數和函式預設都是私有的。可以把整個檔案想像成被一個大型函式包裹起來,形成私有範疇。要公開某個東西,必須明確使用 export 關鍵字:

javascript
// workshop.mjs
var teacher = 'Kyle'; // 私有,外部無法存取

export default function ask(question) {
    console.log(teacher, question);
}

沒有 export 的宣告永遠不會被外部看到。

兩種 import 風格 ​

ES6 模組提供兩種主要的 import 語法,背後代表兩種不同的思維方式。

具名匯入(named import) ​

javascript
import ask from 'workshop.mjs';
ask("It's a default import, right?");
// Kyle It's a default import, right?

這裡把模組預設匯出的函式直接以 ask 這個識別字引入目前的頂層範疇。這種風格類似 Java 的 import 概念:把識別字直接帶進範疇中使用。

命名空間匯入(namespace import) ​

javascript
import * as workshop from 'workshop.mjs';
workshop.ask("It's a namespace import, right?");
// Kyle It's a namespace import, right?

這裡把模組的所有匯出內容收進一個命名空間物件 workshop,再透過它存取各個成員。這種風格把模組本身視為一個具名的命名空間單位,與上一篇介紹的命名空間模式在思維上更為接近。

兩種風格並無對錯之分,只是組織和消費模組的不同視角。Kyle Simpson 個人偏好命名空間匯入,因為這樣可以在程式碼中清楚看出某個函式來自哪個模組。

一個檔案一個模組 ​

ES6 模組是以檔案為單位的。一個檔案只能是一個模組,無法在同一個檔案內定義多個 ES6 模組。這意味著一個由一千個模組組成的應用程式,就是一千個獨立的檔案。

這個特性也決定了若要在瀏覽器中不經過建置工具直接使用,就必須載入大量獨立的網路請求。這也是多數團隊仍搭配打包工具使用 ES6 模組語法的原因。

單例行為 ​

ES6 模組是單例(singleton)。無論一個模組被 import 多少次,它只會執行一次,後續所有的 import 取得的都是同一個實例的參考。

如果需要能建立多個實例的能力,必須在模組的公開 API 中明確匯出一個工廠函式,讓呼叫方自行建立新的實例。這和上一篇介紹的工廠函式模組概念完全一致,只是載體換成了 ES6 模組語法。

為什麼範疇是最重要的支柱 ​

模組模式是詞彙範疇這整條學習路徑的終點站。Kyle Simpson 認為,在 JavaScript 的三個大主題中,Pillar 2(範疇與閉包)是最重要的一個,因為它以最根本的方式觸及其他所有概念。

值得注意的是,模組模式對內部的實作方式沒有限制。你完全可以在一個模組內部大量使用 class,只要最終把這些行為組織進模組,讓外部可以 import 並使用即可。模組模式是程式碼組織層面的決策,它凌駕於內部的實作選擇之上。

複習 ​

ES6 模組在檔案結構上有什麼關鍵特性? ​

ES6 模組以檔案為單位,每個檔案只能存在一個模組,無法在同一個檔案內定義多個 ES6 模組。

在 ES6 模組中,如何讓某個宣告成為公開的? ​

使用 export 關鍵字可以讓宣告成為公開的,沒有被 export 的所有內容都維持私有。

ES6 模組在模組實例化方面有什麼獨特行為? ​

ES6 模組是單例,無論被 import 多少次,模組只會執行一次,後續的 import 都取得同一個實例的參考。

Node.js 建議使用什麼副檔名來支援 ES6 模組? ​

建議使用 .mjs 副檔名,以便在 Node.js 中使用 ES6 模組並與 CommonJS 模組區分。

ES6 模組的範疇預設可見性為何? ​

ES6 模組中的所有內容預設都是私有的,可以把整個模組想像成被包裹在一個私有範疇中。

小測驗 ​

ES6 模組在檔案結構上的主要特性是什麼? 模組以檔案為單位,每個模組必須是獨立的檔案
ES6 模組如何處理模組實例? 模組是單例,只會執行一次
在 ES6 模組中,使用哪個關鍵字讓內容成為公開的? export
Node.js 中建議用於 ES6 模組的副檔名是什麼? .mjs
ES6 模組中,變數和函式的預設可見性為何? 私有(private)

此文章是 FrontendMasters 上的 Deep JavaScript Foundations, v3 課程筆記

最後更新時間:

Buy Me A Coffee

系列章節 第 55 篇 / 共 69 篇

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog
Photo by Federica Galli on Unsplash