點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Closure 實現 Module Pattern

Sam Xiao's Avatar 2021-05-04

當 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 的原理

module000

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 重新命名。

module001

IIFE

Default Export

let f = (() => {
  let count = 0

  let inc = () => ++count

  return inc
})()

f() // ?
f() // ?

Module Pattern 另外一個常見變形是配合 IIFE 直接建立 function,如此則不必再為 module 命名。

module002

Named Export

let { inc: f } = (() => {
  let count = 0

  let inc = () => ++count

  return {
    inc
  }
})()

f() // ?
f() // ?

IIFE 也可搭配 named export 直接對 function 重新命名。

module003

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