模組系統
JavaScript語言長期以來並未內建支援模組系統,社群上發展了兩套知名的模組系統,但它們並不相容:
CommonJS Modules
Asynchronous Module Definition (AMD)
ES6中加入了模組系統的支援,它採用了CommonJS與AMD的優點,成了未來JavaScript語言中重要的特性。
模組系統是什麼?
當程式碼愈寫愈多,應用程式的規模愈來愈大時,我們需要一個用於組織與管理程式碼的方式,這個需求相當明確,或許不只是應用程式發展到一定程度才會考慮這些,而是應該在開發程式之前的規劃就需要考量進來。
JavaScript語言是一個沒有命名空間設計的程式語言,也沒有支援類似的組織與程式碼分離的設計。有些人認為使用物件定義的字面文字,可以定義出物件的方法與屬性,但如果你看過"物件"、"this"與"原型物件導向"的章節內容,就知道物件中並沒有區分私有、公開成員或方法的特性,這個組織法頂多只是把方法或屬性整理集中而已。
而在很早之前(2003)在社群上發展出一個稱之為模組樣式(module pattern),以及之後的變型如 暴露模組樣式(Revealing Module Pattern),就是第一期的程式碼組織方式。模組樣式實作相當簡單,有許多早期開始發展的函式庫或框架採用這個樣式,甚至到今天也可以看到它的使用身影。一個簡單的範例如下(以下範例來自jQuery):
它使用了IIFE函式的特性,區分出作用域,不過它並沒有辦法徹底解決問題,它在小型的應用程式可以用得很好,但在複雜的程式中仍然有很大的問題,例如以下的問題:
沒辦法在程式中作模組載入
模組之間的相依性不易管理
異步載入模組
除錯與測試都不容易
在大型專案中不易管理
模組樣式似乎是一個暫時性的解決方案,但不得不說它的確是上一代很重要的程式碼組織方式,第二代的模組系統,是在2009年之後的CommonJS與AMD計劃,它們實作出真正完整的模組系統,CommonJS是專門設計給伺服器端的Node.js使用的,而AMD的目標對象則是瀏覽器端。當然它們兩者的設計有所不同,也不相容,使用時也可能需要搭配載入工具來一併使用,不過這個階段的模組系統已經是較前一代完善許多,在相依性與模組輸出與輸入,都有相對的解決方式,程式碼的管理與組織方便了許多。
CommonJS與AMD並不會在這裡討論,我們的重點是是ES6中的模組系統,ES6中加入了模組系統的支援,它採用了CommonJS與AMD的優點,是一個語言內建的模組系統,而且它可以使用於瀏覽器與伺服器端,這是一個重大的新特性,可以讓你的開發日子更輕鬆許多。
模組如何開始使用
ES6的模組系統使用上相當簡單,大致上只有三個重點:
ES6的模組程式碼會自動變成strict-mode(嚴格模式),不論你有沒有使用"use strict"在程式碼中。
ES6的模組是一個檔案一個模組
ES6模組使用export(輸出)與import(輸入)語句來進行模組輸出與輸入。輸出通常位於檔案最後,輸入位於最前面。
模組輸出與輸入
有寫成模組的程式碼檔案,才能讓其他程式碼檔案進入輸入。模組輸出可以使用export關鍵字,在想要輸出(也就是變為公開部份)加在前面,物件、類別、函式與原始資料(變數與常數)都可以輸出,例如以下的範例:
多個輸出名稱
上面稱之為多個輸出名稱的情況,有兩種方式可以進行輸入,一種是每個要輸入的名稱都需要定在在花括號({})之中,例如以下的範例:
另一種是使用萬用字元(*),代表要輸入所有的輸出定義的值,不過你需要加上一個模組名稱,例如下面程式碼中的myModule
,這是為了防止命名空間的衝突之用的,之後的程式碼中都需要用這個模組名稱來存取輸出模組中的值,這個作法不常使用:
單一輸出名稱
這個要輸出成為模組的程式碼檔案中,只會有一個輸出的變數/常數、函式、類別或物件,通常會加上default
關鍵詞。如果要使用有回傳值的函式,通常也是用單一輸出的方式。例如以下的範例:
對單一輸出的模組就不需要用花括號,這代表只輸入以default
值定義的輸出語句:
這是最特別的,可以在輸入時改變輸入值的名稱,這樣可以讓作輸入檔案中,確保不會有名稱衝突的情況:
合法的輸出語法
合法的輸入語法
參考資料
Last updated