题目 14 / 14
不用 let,用 IIFE 能修复这个 setTimeout 循环吗?
知识点: 闭包
立即调用一个函数并把 i 作为参数传入,会让每次迭代都拥有自己独立的参数绑定。
使用执行可视化器解题在下方逐步观察每个绑定、函数调用和控制台输出。预测输出结果
预测输出。循环仍然用的是 var —— 把回调包在 IIFE 里会改变每个定时器打印的值吗?
for (var i = 0; i < 3; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 100);
})(i);
}查看答案与完整解析
正确输出
0
1
2为什么会这样
每次迭代时,(function(i) {...})(i) 都会被立即调用,并传入共享循环变量当时的值,把这个值复制进一个全新的、名为 i 的参数绑定中。
setTimeout 的回调闭包捕获的是这个 IIFE 的参数 i,而不是外层循环的 i,因此三个回调各自捕获的是不同的、私有的绑定。
循环结束后定时器触发时,每个回调都读取自己独立的参数绑定,打印出调用时传入的值:先是 0,然后是 1,最后是 2。
这道题考察什么
- IIFE 的实参会为每次调用创建一个全新绑定
- 内部回调闭包捕获的是该参数,从而遮蔽外层的 var
- 这是 ES6 之前用来模拟 let 按迭代作用域的经典写法
运行环境前提
可视化器把每次 IIFE 调用都建模为独立的函数调用,各自拥有自己的活动对象(Activation Object),因此三个 i 参数绑定在任何标准 JavaScript 运行时中都不会互相冲突。