JavaScript 在模組的發展上,有著一段混亂的歷史,正如〈名稱空間管理〉中看到的,現在有 CommonJS、AMD 等模組標準,然而,亦存在著各式的變體,這使得不同標準之間的模組若要互相合作,存在著一定的困難度。
ES6 納入了模組規範,就在於試圖解決這類的問題,正如同 ES6 納入類別語法,在可以用它解決需求的情況下,應該採用以增加互通性,面對 ES6 的模組方案也是如此,在可以用它解決模組需求的情況下,當然是儘可能使用。
然而,在前端或後端,對於 ES6 模組的支援相對來說,是比較慢的,以 Node.js 來說,目前處於實驗階段,在 8.5 之後,必須使用 --experimental-modules
打開才能使用。
對於 ES6 模組來說,一個 .js 是一個模組檔案,然而由於 ES6 模組與原本 Node.js 的模組有在載入機制與靜態分析上並不相同,Node.js 必須有方式可以區分,這是 Node.js 既有的模組,或者是 ES6 的模組,因而對於 ES6 模組,暫時使用了個 .mjs 副檔名作為區別。
在瀏覽器上,要載入 ES6 模組,同樣是透過 <script>
標籤,然而 type
屬性的值是 "module"
,這讓瀏覽器知道這會是個 ES6 模組,這之後在談到瀏覽器上的 JavaScript 操作時會再說明。
總之,一個 .js 檔案是一個 ES6 模組,在當中所有的名稱,作用範圍都侷促在 .js 之中,想要可以被使用的名稱,可以使用 export
來公開,例如,定義一個 math.js 作為模組:
function max(a, b) {
return a > b ? a : b;
}
function min(a, b) {
return a < b ? a : b;
}
function sum(...numbers) {
return numbers.reduce((acc, value) => acc + value);
}
const PI = 3.141592653589793;
const E = 2.718281828459045;
let foo = 'foo';
export {max, min, sum, PI, E};
就這個 math
模組來說,將來其他模組可以使用的,是 max
、min
、sum
、PI
與 E
這些名稱,foo
名稱沒有 export
,它僅在 math
中可用,是 math
模組的私有變數。
如果打算在另一個模組中使用 math
模組,可以使用 import from
,就上面的模組定義來說,你必須知道 export
的名稱是什麼,然後指定 import
哪些名稱:
import {max, sum, PI} from './math';
console.log(max(10, 5)); // 10
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(PI); // 3.141592653589793
雖然 math
模組中 export
了五個名稱,然而,只有被 import
至目前模組的名稱才能使用,若必要,也可以為被 import
的名稱取個別名:
import {max as maximum} from './math';
ES6 希望只有真正需要的名稱,才 import
至目前的模組成為該模組中的名稱,如果想一次從模組中 import
被 export
的全部名稱,必須有個前置名稱參考至一個物件,而被 import
的名稱,都會是該物件上的特性:
import * as math from './math';
console.log(math.max(10, 5));
console.log(math.sum(1, 2, 3, 4, 5));
console.log(math.PI);
一個模組也可以在定義名稱時,同時進行 export
:
export function max(a, b) {
return a > b ? a : b;
}
export function min(a, b) {
return a < b ? a : b;
}
export function sum(...numbers) {
return numbers.reduce((acc, value) => acc + value);
}
export const PI = 3.141592653589793;
export const E = 2.718281828459045;
在 export
時,也可以為名稱取別名再 export
:
function max(a, b) {
return a > b ? a : b;
}
function min(a, b) {
return a < b ? a : b;
}
function sum(...numbers) {
return numbers.reduce((acc, value) => acc + value);
}
const PI = 3.141592653589793;
const E = 2.718281828459045;
export {max as maximum, min as minimum, sum, PI, E};
被 import
的名稱,無論是否宣告為 const
,都是不可變動(Immutable),試圖重新指定值給它,會引發 TypeError
:
import {maximum, minimum} from './math';
maximum = function() {}; // TypeError: Assignment to constant variable.
ES6 的模組是靜態的,import
或 export
必須是在模組的頂層,也就是說,你不能在 if..else
或者是函式中放 import
或 export
,因為靜態分析時並不執行程式碼。