JavaScript中for循环变量迭代行为及闭包相关疑问
JavaScript中for循环变量迭代行为及闭包相关疑问
嘿,我最近在啃JavaScript闭包的知识点时,碰到了两个对比感拉满的for循环例子,结果和我预期的不一样,想跟大家唠唠这个困惑~
先看第一个例子,这段代码的输出完全符合预期:
for (let i = 0; i < 3; i++) { const log = () => { console.log(i); }; setTimeout(log, 100); }
输出是:
$ 0 $ 1 $ 2
我理解的是,这里用let声明的i是作用域绑定到每一次迭代的——JS引擎会在每次循环迭代时,都给i创建一个新的变量引用,而不是所有迭代共用同一个i。所以每个log函数作为闭包,捕获的都是当前迭代里i的专属引用,等setTimeout的回调执行时,拿到的就是对应迭代的i值啦。
但接下来这个例子就把我整懵了:
for (let arr = [1, 2, 3]; arr.length !== 0; arr.pop()) { const log = () => { console.log(arr); }; setTimeout(log, 1000); }
我本来以为会输出:
$ [1, 2, 3] $ [1, 2] $ [1]
结果却是三次输出空数组!这明显说明arr变量是所有迭代共用的,和第一个例子的i完全不一样,这到底是为啥?
后来我想明白了,问题出在for循环的变量初始化逻辑上:
- 第一个例子是标准的三段式for循环(初始化
let i=0、条件判断、增量i++),这种情况下,let声明的变量会在每一次迭代开始前被重新创建并赋值,相当于每次迭代都有一个独立的i。 - 而第二个例子里,
let arr = [1,2,3]这个初始化操作只在循环开始时执行一次,整个循环过程中只有一个arr变量实例!每次迭代的arr.pop()都是在修改同一个数组对象,所有的log闭包捕获的都是这个唯一的arr引用。等1秒后setTimeout执行回调时,for循环早就跑完了——arr已经被pop()到空数组了,所以三次输出都是空的。
如果想要得到预期的输出,其实可以在每次迭代里创建当前数组的副本,让每个闭包捕获独立的数组引用:
for (let arr = [1, 2, 3]; arr.length !== 0; arr.pop()) { // 创建当前数组的副本,而不是直接引用原数组 const currentArr = [...arr]; const log = () => { console.log(currentArr); }; setTimeout(log, 1000); }
这样每个log捕获的是当前迭代的数组副本,就会输出[1,2,3]、[1,2]、[1]啦~
备注:内容来源于stack exchange,提问作者Hayeddine Kelious
相关产品推荐
相关产品推荐

