跳到主要内容

闭包

🛠 代码解析

function createFunctions() {
let funcs = [];
for (var i = 0; i < 3; i++) { // `i` 用 var 声明,属于 `createFunctions` 作用域
funcs.push(() => console.log(i)); // 闭包捕获 `i`
}
return funcs;
}

const myFuncs = createFunctions();
myFuncs[0](); // 3
myFuncs[1](); // 3
myFuncs[2](); // 3

🔹 为什么 myFuncs[0]()myFuncs[1]()myFuncs[2]() 都输出 3

  1. var i 作用域属于 createFunctions() 函数,不是 for 循环的局部变量。
  2. funcs.push(() => console.log(i)); 这个箭头函数不会立即执行,而是把 i 的引用 存入 funcs 数组。
  3. for 循环结束后,i = 3(因为 i++ 使 i === 3 时跳出循环)。
  4. myFuncs[0]()myFuncs[1]()myFuncs[2]() 执行时,它们都访问的是同一个 i,此时 i = 3,所以它们都打印 3

🔬 如何修正?

解决方案 1:使用 let(块级作用域)

function createFunctions() {
let funcs = [];
for (let i = 0; i < 3; i++) { // `let i` 作用域在 `{}` 内,每次循环都是新的 `i`
funcs.push(() => console.log(i));
}
return funcs;
}

const myFuncs = createFunctions();
myFuncs[0](); // 0
myFuncs[1](); // 1
myFuncs[2](); // 2

💡 let 具有块级作用域,每次循环 i 都是一个新的变量(不同作用域),所以 funcs[i] 绑定的是各自的 i 值。


解决方案 2:使用 IIFE(立即执行函数表达式)

function createFunctions() {
let funcs = [];
for (var i = 0; i < 3; i++) {
(function(i) { // 立即执行函数创建一个新的作用域,i 作为参数传入
funcs.push(() => console.log(i));
})(i);
}
return funcs;
}

const myFuncs = createFunctions();
myFuncs[0](); // 0
myFuncs[1](); // 1
myFuncs[2](); // 2

💡 IIFE(立即执行函数表达式)创建了一个新的作用域,使 i 变成局部变量,因此每次循环 funcs.push(() => console.log(i)); 绑定的都是不同的 i


🔍 结论

方案var i 作用域结果
原始代码 (var i)i 共享同一个作用域3, 3, 3(因为 i 最终变成 3
使用 let ii 具有块级作用域,每次循环都是新的变量0, 1, 2
使用 IIFEi 作为参数传递,形成独立作用域0, 1, 2

👉 推荐使用 let,因为它更符合现代 JavaScript 语法,避免了 var 的作用域问题! 🚀