闭包
🛠 代码解析
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
?
var i
作用域属于createFunctions()
函数,不是for
循环的局部变量。funcs.push(() => console.log(i));
这个箭头函数不会立即执行,而是把 对i
的引用 存入funcs
数组。for
循环结束后,i = 3
(因为i++
使i === 3
时跳出循环)。- 当
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 i | i 具有块级作用域,每次循环都是新的变量 | 0, 1, 2 |
使用 IIFE | i 作为参数传递,形成独立作用域 | 0, 1, 2 |
👉 推荐使用 let
,因为它更符合现代 JavaScript 语法,避免了 var
的作用域问题! 🚀