首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

JavaScript 閉包經典面試題:變量捕獲與遞增陷阱

     編輯:程式設計綜合問答

題目代碼

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` 是外層函數 `a` 的局部變量,正常情況下 `a` 執行完應銷毀。
  • 但內部函數引用了 `b`,導致 `b` **逃逸** 到堆上,生命週期延長至內部函數被引用為止。
  • 多次調用 `ac` 共享**同一個** `b` 變量,故 `++b` 產生累加效果。
  • `x` 同理被捕獲,但未修改,故保持 10 不變。

常見誤區

誤區錯誤推理正確理解
以為每次調用重新初始化 b每次 ac(10) 都會重新執行 a(10) 吧?a(10) 只執行一次,返回函數 ac 複用同一閉包環境
忽略前綴 ++ 返回新值++b 是先用後加?++b 是先加後用,返回遞增後的值 (4, 5...)
混淆全局變量 bvar b = ac(10) 會覆蓋閉包裡的 b?外層 var b 是新變量,與閉包內的 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

最佳實踐建議

  • **面試必考**:閉包、變量提升、this 綁定、事件循環是 JS 基礎四大金剛,必須能手寫執行流程。
  • **實戰慎用**:閉包易造成內存洩漏(如循環引用 DOM、大數據長期駐留),用完及時解除引用 (`ac = null`)。
  • **現代替代**:`class` + 私有字段 (`#b`)、`WeakMap`、模塊模式 可更清晰地管理封裝狀態。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved