function a(x) {
var b = 3;
return function(y) {
return x + y + (++b);
}
}
var ac = a(10);
var b = ac(10);
var c = ac(10);
// 求 b、c 的值
1. a(10) 調用:
- 參數 x = 10
- 區域變量 b = 3 (初始化)
- 返回匿名函數 f(y) { return x + y + (++b); }
- 此時形成閉包:f 捕獲了 x=10 和 b=3 的引用
- ac 指向這個閉包函數
2. ac(10) 第一次調用 (賦值給 b):
- 參數 y = 10
- 執行體:x(10) + y(10) + (++b)
- ++b:b 從 3 變為 4,返回 4
- 計算:10 + 10 + 4 = 24
- b = 24
- 閉包中的 b 現在為 4
3. ac(10) 第二次調用 (賦值給 c):
- 參數 y = 10
- 執行體:x(10) + y(10) + (++b)
- ++b:b 從 4 變為 5,返回 5
- 計算:10 + 10 + 5 = 25
- c = 25
- 閉包中的 b 現在為 5
b = 24,c = 25
| 誤區 | 錯誤推理 | 正確理解 |
|---|---|---|
| 以為每次調用重新初始化 b | 每次 ac(10) 都會重新執行 a(10) 吧? | a(10) 只執行一次,返回函數 ac 複用同一閉包環境 |
| 忽略前綴 ++ 返回新值 | ++b 是先用後加? | ++b 是先加後用,返回遞增後的值 (4, 5...) |
| 混淆全局變量 b | var b = ac(10) 會覆蓋閉包裡的 b? | 外層 var b 是新變量,與閉包內的 b 無關,閉包 b 不受影響 |
// 方案一:每次調用 a() 創建新閉包
var b = a(10)(10); // 24
var c = a(10)(10); // 24 (新閉包,b 重新初始化為 3)
// 方案二:用對象封裝狀態,手動重置
function createCounter() {
let b = 3;
return {
calc(y) { return 10 + y + (++b); },
reset() { b = 3; }
};
}
const counter = createCounter();
counter.calc(10); // 24
counter.reset();
counter.calc(10); // 24