ES6學習歷程 11 — 全面了解ES6 modules
全面了解 ES6 modules
ES6模組是只會在嚴格模式下執行的JavaScript腳本,也就是說模組中宣告的任何變數或函式都不會自動添加到全域範圍中。
在網絡瀏覽器上執行模組
首先,建立一個新的文件名為message.js且加入以下程式碼:
export let message = 'ES6 Modules';
message.js是個模組,包含了message這個變數。”export” 將message變數公開給其他模組。
第二,建立另一個文件名為app.js,然後在app.js中使用message.js,如下所示:
import { message } from './message.js'const h1 = document.createElement('h1');h1.textContent = messagedocument.body.appendChild(h1)
第三,建立一個新的HTML頁面,這頁面中使用app.js這模組,如下所示:
<!DOCTYPE html><html>
<head> <meta charset="utf-8"> <title>ES6 Modules</title></head><body> <script type="module" src="./app.js"></script></body></html>
在瀏覽器上便會看到:
輸出 (Exporting)
輸出一個變數、函式、或是class時,必須將export關鍵字放在前面:
// log.jsexport let message = 'Hi';export function getMessage() { return message;}export function setMessage(msg) { message = msg;}export class Logger {}
要注意的一點是,export導出的函式或class必須要有名稱,所以不能使用export導出匿名(anonymous)函式或class。
可以先定義好變數、函式或class,然後再導出 (export):
// foo.jsfunction foo() { console.log('foo');}function bar() { console.log('bar');}export foo;
在上面範例中,foo() 先定義了函式,然後才導出。因為沒有導出bar(),所以無法在其他模組中使用它。bar()函式在模組外部無法使用,因此可以說它是私有的 (private)。
輸入(Importing)
使用export定義模組後,可以使用import關鍵字調用所導出變數、函式和class。
import { what, ever } from './other_module.js';
值得注意的是,當你從模組import綁定時,綁定的行為就像使用const定義那樣。這表示你不能再用這個名稱創建任何組件,也不能更改綁定的值。
看範例:
// greeting.jsexport let message = 'Hi';export function setMessage(msg) { message = msg;}
Import message變數和setMessage()函式時,可以使用setMessage()函式更改message的值。但是,不能直接更改message的值。如下:
// app.jsimport {message, setMessage } from './greeting.js';console.log(message); // 'Hi'setMessage('Hello');console.log(message); // 'Hello'message = 'Hallo'; // error
在執行背後,當你調用setMessage()函式時,JavaScript回到greeting.js模組並執行程式碼來更改message變數。然後,更改自動反映在導入的message綁定上。
在app.js綁定的message是導出的message的本地名稱。因此,本質上來說,app.js中的message 變數跟greeting.js模組中的message 變數是不同的。
導入單個綁定
假設有一個foo變數如下:
// foo.jsexport foo = 10;
然後,在另一個模組中,可以重複使用foo變數:
// app.jsimport { foo } from './foo.js';console.log(foo); // 10;
但是,您無法更改的值foo。如果嘗試這樣做,將會出現錯誤:
foo = 20; // throws an error
導入多個綁定
假設有一個cal.js模組:
// cal.jsexport let a = 10, b = 20, result = 0;export function sum() { result = a + b; return result;}export function multiply() { result = a * b; return result;}
要從cal.js中導入這些綁定的話,可以列出它們,如下所示:
import {a, b, result, sum, multiply } from './cal.js';sum();console.log(result); // 30multiply();console.log(result); // 200
將整個模組導入為物件
要將模組中的所有內容作為單個物件導入,請使用星號(*)模式,如下所示:
import * as cal from './cal.js';
在範例中,從cal.js模組導入了所有綁定作為cal物件。在這種情況下,所有綁定都成為cal物件的屬性,因此可以這樣使用它們:
cal.a;cal.b;cal.sum();
此導入稱為「命名空間導入 (namespace import)」。
重要的是要記住,即使你import很多次,他也只會執行一次。就像以下範例:
import { a } from './cal.js';import { b } from './cal.js';import {result} from './cal.js';
在第一個import之後,cal.js模組就會被執行並載入到memory中,然後在之後的import都是「重複使用」已經存在的cal.js模組。
import和export的局限性
使用import和export時,必須在語句和函式之外使用,否則會導致SyntaxError:
if( requiredSum ) { export sum;} // SyntaxErrorfunction importSum() { import {sum} from './cal.js';} // SyntaxError
Aliasing
當export或import變數、函式、或class時,可以為他們建立別名。看看math.js模組:
// math.jsfunction add( a, b ) { return a + b;}export { add as sum };
因此,當從math.js模組導入add()函式時,必須改用sum這個名稱:
import { sum } from './math.js';
如果要在導入時使用其他名稱,則可以使用as關鍵字:
import {sum as total} from ‘./math.js’;
重新導出綁定
可以導出已導入的綁定。這稱為重新導出。例如:
import { sum } from './math.js';export { sum };
上述中, sum從math.js模組導入並重新導出。以下語句與上述語句等效:
export {sum} from './math.js';
如果要在重新導出之前重新命名綁定的話,就要使用as關鍵字:
export { sum as add } from './math.js';
如果要從另一個模組導出所有綁定,則可以使用星號(*):
export * from './cal.js';
默認導出
一個模組只能有一個默認導出。默認導出更易於導入。模組的默認值可以是變量、函式或class。
以下是具有默認導出的sort.js模塊。
// sort.jsexport default function(arr) { // sorting here}
不需要指定函式名稱,因為模組就代表函式名稱。
import sort from sort.js;sort([2,1,3]);
如同以上程式碼,sort代表sort.js模組的默認函式。注意的是,不需要使用 { } 花括號將sort 包起來。
sort.js模組包含默認導出和非默認導出:
// sort.jsexport default function(arr) { // sorting here}export function heapSort(arr) { // heapsort}
要同時導入默認和非默認綁定,請在import關鍵字後使用以下規則:
- 默認綁定必須放在第一位。
- 非默認綁定必須用花括號括起來。
看範例:
import sort, {heapSort} from './sort.js';sort([2,1,3]);heapSort([3,1,2]);
若要重新命名默認導出,還可以使用as關鍵字:
import { default as quicksort, heapSort} from './sort.js';