我已经阅读了MDN文档,我真的可以说这是我第一次在一段时间内感到困惑。
参考以下代码:
for (
let i = 0, getI = () => i, incrementI = () => i++;
getI() < 3;
incrementI()
) {
console.log(i);
}
// Logs 0, 0, 0
我看到第一次迭代:
- for循环将
i
初始化为0
- 创建
i
的闭包 - 创建
i++
的闭包 - 调用
getI
函数并返回0
,执行继续条件,循环执行console.log(i)
- 是否在
console.log(i)
之后创建了新的词法作用域?初始的i
变量消失了吗? getI
函数得到定义为每个新的迭代?
- 是否在
- 调用
incrementI
,执行i++
- 是一个新的
i
变量创建,以前的i
复制到新的i
,然后新的i
增加?
- 是一个新的
- 下一次迭代开始
- 如果生成了一个新的
i
,并且增加了新的值,为什么console.log(i)
仍然指向初始的i
,即0?
- 如果生成了一个新的
我在MDN中阅读了关于for
的文档,但是我找不到代码片段中显示的行为的解释。为什么记录0
,0
,0
而不是0
,1
,2
?
答案就在文档的这一部分
初始化块的作用域效应可以理解为声明发生在循环体中,但恰好在条件和事后考虑部分中可访问。更准确地说,let声明是for循环的特殊情况-如果初始化是let声明,则每次在循环体求值后,都会发生以下情况:
- 使用新的let声明的变量创建新的词法作用域。
- 使用上次迭代的绑定值来重新初始化新变量。
- 事后在新范围内评估。
例如,在循环的每次迭代之后,用前一个的值声明一个新的i
。然后对afterthought
求值。但是,在您的情况下,getI
和incrementI
在原始i
上关闭,原始i
被增加并检查(因此循环最终停止),但下一次调用console.log
总是使用"new"i
,它被初始化为0
(即在之前的前一个迭代的值被计算),然后永远不会增加。
如果你想让这个函数工作,只需将i
的初始化放在for
之外。
let i;
for (
i = 0, getI = () => i, incrementI = () => i++;
getI() < 3;
incrementI()
) {
console.log(i);
}