什麼是閉包(Closure)?
閉包是 JavaScript 中的一個重要概念,指的是函式在創建時,能夠記住並存取其外部作用域的變數,即使該作用域已經執行完畢。這種特性使得 JavaScript 的函式可以擁有「記憶」的能力,允許函式保持對外部變數的存取權。
閉包的概念建立在 JavaScript 的詞法作用域(Lexical Scope)之上,也就是函式可以存取其被定義時所在的作用域中的變數,而不是函式執行時的作用域。
1. 基本範例
function outerFunction() { |
解析
outerFunction
內部宣告了一個變數outerVariable
,並定義了一個innerFunction
。innerFunction
存取outerVariable
,並被outerFunction
返回。- 當
closureExample
執行時,即使outerFunction
早已執行完畢,innerFunction
仍然能存取outerVariable
,這就是閉包的作用。
2. var
變數與閉包的問題
JavaScript 在 ES6 之前使用 var
來宣告變數,但 var
具有函式作用域(Function Scope),這可能會導致閉包相關的陷阱。
問題:for
迴圈與 var
function varClosureExample() { |
為什麼會這樣?
var
是函式作用域,它在整個varClosureExample
內部是共享的。- 當
closures[0]()
、closures[1]()
和closures[2]()
執行時,它們都參考同一個i
,但i
已經變成3
(因為for
迴圈已經執行完畢)。 - 所有函式都會輸出
3
,而不是0, 1, 2
。
3. 如何修正 var
的問題
解決方案 1:使用 let
(區塊作用域)
function letClosureExample() { |
為什麼 let
有效?
let
具有區塊作用域(Block Scope),每次迴圈執行時,i
都是一個新的變數,因此閉包綁定的i
值不會變化。
解決方案 2:使用 IIFE(立即執行函式)
如果只能使用 var
,可以使用 IIFE(Immediately Invoked Function Expression):
function varClosureFixedExample() { |
為什麼 IIFE 有效?
- IIFE 會立即執行,並建立一個新的作用域,確保每次迭代時
i
都是獨立的。
4. 閉包的實際應用
(1) 模擬私有變數
function createCounter() { |
解析
count
是createCounter
內部變數,外部無法直接存取,只能透過increment
和decrement
方法修改,形成了私有變數的概念。
(2) 函式工廠(Function Factory)
function createMultiplier(multiplier) { |
解析
double
變數是一個閉包,記住了multiplier
為2
。triple
變數是一個閉包,記住了multiplier
為3
。- 這種模式可以用來建立彈性的函式生成器。
(3) 延遲執行與事件處理
function delayedMessage(message, delay) { |
解析
setTimeout
內部的函式形成閉包,記住message
變數,即使delayedMessage
已經執行完畢。
5. 總結
閉包的核心概念
- 函式可以存取其外部函式的變數,即使外部函式已經執行完畢。
var
變數的作用域為函式作用域,可能導致閉包內變數的值不如預期。- 使用
let
或 IIFE 可以避免var
造成的閉包問題。
閉包的應用
- 私有變數(避免全域變數污染)
- 函式工廠(建立可重複使用的函式)
- 事件處理與回調函式
- 延遲執行與計時器
閉包是 JavaScript 的核心概念之一,它允許函式記住外部變數,即使作用域已經離開。var 宣告的變數可能會導致作用域問題,例如 for 迴圈內的閉包錯誤,而 let 或 IIFE 可以解決這個問題。
掌握閉包後,可以更進一步學習 JavaScript 的函式式程式設計(Functional Programming),提升程式的模組化與可讀性。