JavaScript 模組系統

深入淺出學習 JavaScript 模組系統

Jacy Chu
6 min readJul 5, 2021

📋 目錄

  1. JavaScript 模組介紹
  2. ⭐️ CommonJS
  3. ⭐️ AMD & CMD
  4. ⭐️ UMD
  5. ⭐️ ESM
  6. JavaScript 模組比較
  7. 小結

JavaScript 模組介紹

模組化幫助開發者拆分和組織程式碼。隨著技術的發展,程式碼量越來越大,需要對程式碼有更好的管理,而模組化能夠幫助開發者解決命名衝突、管理依賴、提高程式碼的可讀性、程式碼解耦以及提高程式碼的複用性。將一個龐大複雜的功能拆分成各個獨立小功能過程稱為模組化,其中每個小功能即是模組。
JavaScript 語言長期以來並未內建支援模組系統,社群上發展了兩套知名的模組系統,但它們彼此並不相容:(1) CommonJS Modules (2) Asynchronous Module Definition ,近年 ES6 中加入了模組系統的支援,它採用了兩者的優點,成了未來 JavaScript 語言中重要的特性。

在探索各種 modules 之前,先快速複習幾個先備知識 🔬

(1) 閉包 Closure

  • 定義在一個函數內部的函數
  • 函式以及該函式被宣告時所在的作用域環境組合
  • 函數外部無法讀取函數內部宣告的變數,透過在函數的內部,再定義一個函數,存取內部定義的變數
  • 閉包最大的特點,就是它可以記住誕生的環境,即閉包可以使得它誕生環境一直存在
  • 閉包的兩個最大用處: (1) 可以讀取外層函數內部的變數(2) 讓這些變量始終保持在記憶體中

詳細閉包可以參閱 MDN Web Docs 說明 🔎

(2) 函式調用 Function invocation

在函式調用,JS 提供不同的方法:(), call(), apply(), bind()

  • () : 直接調用 function,不明確指定 this
  • call() :調用 function,明確指定 this
    fn.call(this,arg1,arg2...,argn)
  • apply() : 調用 function,明確指定 this
    call() 或 apply() 主要差異在第二個參數
    fn.apply(this,[arg1,arg2...,argn])
  • bind() :明確指定 this ,回傳一個函式(其綁定所指定的 this)
    this 一但被綁定,就無法再被修改

(3) 立即呼叫函式表達式 IIFE

  • IIFE 內部形成單獨作用域,封裝內部變數為私有變數,避免被外部存取
  • 不必為函數命名,避免污染全局變數

下方為沒有代入參數的 IIFE:

(function() {[code]}());

下方為代入 x 參數,代入 function 時命名成 a:

(function(a) {[code]}(x));

下方 function(a) 為參數,代入時命名成 factory
* factory 代表 (function(a){[code]})

(function(factory) {// 調用 factory}((function(a) {[code]})));

IIFE 在下方介紹 UMD 時會用到 ⛳️

⭐️ CommonJS

Nodejs 環境所使用的模組系統是基於 CommonJs 規範實現。
以服務器(瀏覽器之外)為主的 runtime 所使用,為同步加載(依順序載入套件),模組輸出是值的拷貝。

把要匯出的 function 變數 exports出去。在引入時,使用 require 引入。
參閱詳細文件 🔎

⭐️ AMD & CMD

AMD (Asynchronous Module Definition)
ES6 出現前,官方並沒有提供模組包裝。RequireJS 團隊推出 AMD Library: RequireJS,協助包裝程式碼給第三方使用。過程中需要先設定好,瀏覽器會根據執行環境將這些檔案找出來,並做相依性的檢查。

在沒有使用 AMD 情況下,需將每個檔案分別 source 進來。使用 AMD 需要先將需要 source 的檔案用 define 定義好,並引用 require.js 檔案,main.js 檔案將定義好的檔案 require 照依賴序放入參數(會被其他檔案引用的,必須放前面)。由於AMD 為預先下載、依賴前置,執行到 main.js 會預先把所以定義檔案下載執行好,即使後續沒有用到也會先下載執行。

CMD (Common Module Definition)
SeaJS 團隊推出 CMD 非同步模組化 Library:SeaJS,協助包裝程式碼給第三方使用。CMD 與 AMD 語法相似,差異在於 CMD 為預先下載、依賴就近(延遲執行),代表設定好的檔案在需要被執行時,才會執行。如此一來,完全沒用到的檔案即不會執行到,節省效能跟空間。

⭐️ UMD

UMD (Universal Module Definition)
UMD 並非定義匯出匯入的規範,而是相容AMD規範、commonJS規範及全域性引用方式的寫法。

⭐️ ESM

ESM (ES Modules)
JS 官方 ES6 中加入了模組系統的支援,採用 CommonJS 與 AMD 的優點,採用按需載入(編譯時載入),加入 Tree shaking 功能(不會加載所有方法,僅取所需),成為瀏覽器與服務器通用之解決方案。ES6 使用 import 匯入、 export 匯出,輸出的是值的引用。

JavaScript 模組比較

自從 JS 官方推出 ESM,ESM 的方式會是較好的模組系統。然而面對到許多過去開發者撰寫之模組化程式碼,清楚理解各個模組形式有其必要。
下表為各個模組之間的比較:

小結

先理解何謂模組化,並簡單複習幾個先備知識,再細細拆解各個模組的撰寫模式,最後附上模組比較表格。此篇文章脈絡收在辨別個模組相異之處,有關如何打包模組,筆者搜尋一篇不錯的文章:Rollup 筆記 (由 PJchender 撰寫)。
有任何指點或建議都可以在下方留言~🙏

📃 Thanks for reading.

--

--