JS闭包示例解析:let循环变量的闭包求值疑问
搞懂var和let在for循环里的作用域差异
首先先快速回顾你提到的var的情况,帮咱们对比着看:
function createFunctionsWithVar() { var arr = []; for (var i = 0; i < 5; i++) { arr.push(function() { console.log(i); }); } return arr; } const funcsVar = createFunctionsWithVar(); funcsVar[0](); // 输出5,所有函数都共享同一个i
这是因为var的作用域是函数级的,整个循环里只有一个i变量,所有push进去的函数都捕获的是这个同一个i的引用。当循环结束时i已经变成5,所以后续调用任何函数都会输出5。
接下来重点说你困惑的let的情况:
你觉得“此时i已无定义”其实是个误解,咱们看实际运行的代码:
function createFunctionsWithLet() { let arr = []; for (let i = 0; i < 5; i++) { arr.push(function() { console.log(i); }); } return arr; } const funcsLet = createFunctionsWithLet(); funcsLet[0](); // 输出0 funcsLet[1](); // 输出1 funcsLet[2](); // 输出2 funcsLet[3](); // 输出3 funcsLet[4](); // 输出4
为什么会这样?这是ES6给let(还有const)在for循环头部声明时设计的特殊绑定规则:
- 每次循环迭代,JavaScript都会为
i创建一个全新的变量绑定,而不是像var那样复用同一个变量。 - 每个push到数组里的函数,捕获的都是当前迭代的那个专属的
i引用。 - 当后续调用这些函数时,它们会从自己捕获的词法环境中取出对应的
i值,也就是当时循环迭代时的数值,所以能正确输出0到4,根本不会出现“i无定义”的情况。
简单说,let在for循环头部的行为,相当于把变量的作用域缩小到了每一次循环迭代的块级作用域里,每个迭代的i都是独立的,闭包自然就能保存住对应的值了。
内容的提问来源于stack exchange,提问作者DCR
相关产品推荐
相关产品推荐

