當 Function 回傳 Function 時,Function 外的 Variable 將保存隨其 Function 改變,此為使用 Closure 的 Module Pattern,事實上 Vue 3 的 Module 就是 Module Pattern 應用。
Version
ECMAScript 2015
Module Pattern
Default Export
let myModule = () => {
let count = 0
let inc = () => ++count
return inc
}
let f = myModule()
f() // ?
f() // ?
let g = myModule()
g() // ?
g() // ?
第 1 行
let myModule = () => {
let count = 0
let inc = () => ++count
return inc
}
myModule
為 function,在其內定義 count
state 與 inc()
,最後只將 inc()
回傳回去,count
state 被封裝在 myModule
內。
第 9 行
let f = myModule()
f() // ?
f() // ?
let g = myModule()
g() // ?
g() // ?
由 myModule()
各自產生 f()
與 g()
,可發現 f()
與 g()
會各自維護 count
state 而不會互相影響,因為 count
各自保留在 f()
與 g()
的 Closure 內。
這也是 Vue 3 的 module 能將 component 共用部分抽成 module,又能保持 component 有各自 state 的原理
Named Export
let myModule = () => {
let count = 0
let inc = () => ++count
return {
inc
}
}
let { inc: f } = myModule()
f() // ?
f() // ?
let { inc: g } = myModule()
g() // ?
g() // ?
第 1 行
let myModule = () => {
let count = 0
let inc = () => ++count
return {
inc
}
}
若要在 Module Pattern 使用 named export,則要將 function 包成 Object 回傳。
11 行
let { inc: f } = myModule()
f() // ?
f() // ?
如此可用 object destructure 取得 inc()
,可同時對 function 重新命名。
IIFE
Default Export
let f = (() => {
let count = 0
let inc = () => ++count
return inc
})()
f() // ?
f() // ?
Module Pattern 另外一個常見變形是配合 IIFE 直接建立 function,如此則不必再為 module 命名。
Named Export
let { inc: f } = (() => {
let count = 0
let inc = () => ++count
return {
inc
}
})()
f() // ?
f() // ?
IIFE 也可搭配 named export 直接對 function 重新命名。
Conclusion
- Module Pattern 為 JavaScript 很重要 pattern,這使得 JavaScript 在不需要 class 下也能實現 Encapsulation
- Vue 3 的 module 其實就是 Module Pattern 應用,讓 component 既能共用 JavaScript 又能有各自 state
- Module Pattern 實務上常搭配 IIFE 使用,如此只要一行就可建立 function 且具備 Encapsulation
Reference
Coder’s Toolbox, You can’t understand JavaScript without mastering Closure