ES Module 是 ECMAScript 2015 最重要功能,讓 ECMAScript 總算有標準 Module,且語法只有 import
與 export
,非常精簡。
Version
macOS Catalina 10.15.1
VS Code 1.40.1
Quokka 1.0.261
ECMAScript 2015
ES Module
可將 data (variable、object、function、class) 加以 import 或 export。
Export 分為 named export 與 default export:
- Named Export:data 必須有名稱
- Default Export:data 沒有名稱 (anonymous object、anonymous function、anonymous class)
- 一個 module 只能有一個 default export,但能有無限多個 named export
Default export 的 data 也可以有名稱,但因為會由 import 決定名稱,所以通常會使 data 沒名稱
Named Export
Variable
export let title = 'FP in JavaScript'
export const price = 100
可直接對 let
與 const
變數加以 export 。
import { title, price } from './my-module'
title // ?
price // ?
可對 variable 分別 import
,但 named Import 要搭配 {}
。
為什麼可以使用
{}
object destructuring 語法呢 ?
let title = 'FP in JavaScript'
let price = 100
export {
title,
price
}
事實上直接對 variable export,相當於使用 property shorthand 先整理成 anonymous object 後才 export。
import { title, price } from './my-module'
title // ?
price // ?
因此 import
才可以使用 {}
object destructuring。
Object
export let obj = {
title: 'FP in JavaScript',
price: 100
}
也可直接對 object 加以 export。
import { obj } from './my-module'
obj.title // ?
obj.price // ?
可對 object 直接 import,使用 object destructuring 對 object 解構。
import { title, price } from './my-module'
title // ?
price // ?
直覺會想用 object destructuring 將 property 取出,但事實上並非標準語法。
let obj = {
title: 'FP in JavaScript',
price: 100
}
export {
obj
}
根據剛才經驗,export 會整理成 object。
import {{ title, price }} from './my-module'
title // ?
price // ?
所以應該要兩層 object destructuring 才合乎邏輯,但這種寫法可讀性不高,而且 ES6 也不允許這樣寫。
import { obj } from './my-module'
let { title, price } = obj
title // ?
price // ?
若堅持要使用 object destructuring,應該另外使用 let
,而不能在 import
一次解構。
Function
export function add(x, y) {
return x + y
}
將 add()
加以 export。
import { add } from './my-module'
add(1, 1) // ?
對 function 加以 named import 要加上 {}
。
export let add = (x, y) => x + y
也可將 arrow function 加以 export。
import { add } from './my-module'
add(1, 1) // ?
對 arrow function 加以 named import 也要加上 {}
。
Class
export class Counter {
constructor(x, y) {
this.x = x
this.y = y
}
sum() {
return this.x + this.y
}
}
將 Counter
class 加以 export。
import { Counter } from './my-module'
let counter = new Counter(1, 1)
counter.sum() // ?
對 class 加以 named import 要加上 {}
。
無論對 variable / object / function / class 加以 named export,都會事先明確命名,然後在 named import 時都加上
{}
object destructuring,因為export
會先湊成 object 再 export
Default Export
Variable
ES 6 無法對 var
、let
與 const
使用 default export,因為並不存在 anonymous variable。
Object
export default {
title: 'FP in JavaScript',
price: 100
}
對於 anonymous object 可使用 default export。
import MyObject from './my-module'
MyObject.title // ?
MyObject.price // ?
由於 anonymous object 本來就沒有名稱,因此 default import 要重新命名。
import { title, price } from './my-module'
title // ?
price // ?
與 named import 一樣,不存在直接在 import
做 object destructuring。
import MyObject from './my-module'
let { title, price } = MyObject
title // ?
price // ?
若堅持要使用 object destructuring,應該另外使用 let
,而不能在 import 一次解構。
Function
export default function(x, y) {
return x + y;
}
對於 anonymous function 可使用 default export。
import add from './my-module'
add(1, 1) // ?
由於 anonymous function 本來就沒有名稱,因此 default import 要重新命名。
export default (x, y) => x + y
對於 arrow function 可使用 default export。
import add from './my-module'
add(1, 1) // ?
由於 arrow function 本來就沒有名稱,因此 default import 要重新命名。
Class
export default class {
constructor(x, y) {
this.x = x
this.y = y
}
sum() {
return this.x + this.y
}
}
對於 anonymous class 可使用 default export。
import Counter from './my-module'
let counter = new Counter(1, 1)
counter.sum() // ?
由於 anonymous class 本來就沒有名稱,因此 default import 要重新命名。
無論對 variable / object / function / class 加以 default export,可不用事先明確命名 (當然要事先命名亦可,但沒有太大意義),然後在 named import 時不用加上
{}
Named + Default Export
一個 Module 只允許一個 default export,但可以有多個 named export。
export let title = 'FP in JavaScript'
export let mul = (x, y) => x * y
export default class {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
return this.x + this.y;
}
}
title
與 mul
為 named export 可以多個,但 anonymous class 為 default export只有一個。
import { title, mul } from './my-module'
import Counter from './my-module'
title // ?
mul(1, 2) // ?
let counter = new Counter(1, 1)
counter.sum() // ?
Named export 要搭配 named import,而 default export 則搭配 default export。
Import Entire Module
實務上一個 module 可能有很多 export,要一個一個 import 很辛苦,也可將整個 module 都 import 進來。
import * as MyModule from './my-module'
MyModule.title // ?
MyModule.mul(1, 3) // ?
let counter = new MyModule.default(1, 1)
counter.sum() // ?
對於 named export 沒問題,名稱會維持原來名稱。
但對於沒有名稱的 default export,會以 default
為名稱。
Alias
若對原本 data 名稱覺得不滿意,在 named export 或 named import 時都可以重新取別名。
let add = (x, y) => x + y
export { add as sum }
在 named export 時,可使用 as
將 add
取別名為 sum
,但需搭配 {}
。
import { sum } from './my-module'
sum(1, 1)
既然已經取別名為 sum
,就可以 sum
為名稱 import 進來。
export let add = (x, y) => x + y
直接使用 named export 將 add()
export 出來。
import { add as sum } from './my-module'
sum(1, 1) // ?
在 named Import 時才使用 as
取別名亦可。
Conclusion
- ES6 module 分成 named export 與 default export,一個 module 只能有一個 default export,但可以有多個 named export
export
與import
還可搭配as
取別名
Reference
John Resig, Secret of the JavaScript Ninja, 2nd
MDN, export
MDN, import