Closure

JavaScript 的記憶體管理

垃圾回收器(garbage collector) 系統

  • 追蹤記憶體分配的使用情況,以便自動釋放一些不再使用的記憶體空間
  • 全域變數關閉瀏覽器頁面,才會回收
  • 區域變數的值,沒有用到就會被自動回收

簡單理解

  • 閉包 = 內層函式 + 外層函式的變數
  • 當內層函式用到了外層的變數,就構成了閉包
  • 內部函式除了能夠取得函式外部的變數,還可以記住這個變數

範例 1

function outer() {  const a = 1  function f() {    console.log(a)  }  f()}outer() // 1

範例 2

function outer() {  let i = 1  function fn() {    console.log(i)  }  return fn}const fun = outer()fun() // 1

應用 1 — 狀態保存

  • 通常會 return 一個匿名函式,可以取得到外層函式的變數
  • 用一個變數來取出回傳的函式
  • 因為 result 指向 count 這個函式(全域),myCount不會被回收
  • myCount 計算後的結果會被儲存下來

範例

//function count() {  let myCount = 0  return function () {    myCount++    console.log(`被調用${myCount}次`)  }}const result = count()result() // "被調用1次"result() // "被調用2次"

應用 2 — 緩存機制

  • 因為閉包原理,cache 變數可以被 return 的函式取得
  • 能夠用cache來放想要緩存的東西,在執行前先檢查是否存在快取
  • 可以避免重複計算
function expensiveFunction() {  // 這裡是複雜的操作  console.log('Executing expensive operation...')  return 'Expensive Result'}function createCache() {  let cache = {}  return function (key, func) {    if (!cache[key]) {      cache[key] = func()    }    return cache[key]  }}const cachedFunction = createCache()// 首次調用,將計算結果緩存// 輸出: Executing expensive operation... Expensive Resultconsole.log(cachedFunction('test', expensiveFunction))// 第二次調用,直接從緩存回傳結果// 輸出: Expensive Result(不會再次執行 expensiveFunction)console.log(cachedFunction('test', expensiveFunction))

應用 3 — 模擬私有變數

  • 開發的程式碼內部細節,並不想讓外部來獲取時
  • 可以避免變數被直接修改,維持穩定性
function createBankAccount(initialBalance) {  let balance = initialBalance // 私有變數  return {    deposit: function (amount) {      balance += amount      console.log(`存款後餘額:${balance}`)    },    withdraw: function (amount) {      if (amount <= balance) {        balance -= amount        console.log(`提款後餘額:${balance}`)      } else {        console.log('餘額不足')      }    },    getBalance: function () {      return balance // 提供對私有變數的讀取方式    }  }}const account = createBankAccount(100)account.deposit(50) // 存款後餘額:150account.withdraw(20) // 提款後餘額:130console.log(account.getBalance()) // 130console.log(account.balance) // undefined,無法直接訪問私有變數

需注意

  • 當使用閉包時,因為變數會常駐在記憶體中不會清除
  • 若使用過多,可能導致內存泄露(memory leak)

參考資料

# JavaScript # 閉包