我知道let
可以防止重复声明,这很好。
let x;
let x; // error!
用let
声明的变量也可以用在闭包中,这是可以预料到的
let i = 100;
setTimeout(function () { console.log(i) }, i); // '100' after 100 ms
我有点难以理解的是let
是如何应用于循环的,这似乎是for
循环特有的。
// prints '10' 10 times
for (var i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
// prints '0' through '9'
for (let i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
为什么在这种情况下使用let
可以工作?在我的想象中,即使只有一个块是可见的,for
实际上为每个迭代创建了一个单独的块,并且let
声明是在那个块内部完成的...但是只有一个let
声明来初始化值。这只是ES6的语法糖吗?它是如何工作的?
我理解var
和let
之间的区别,并且已经在上面举例说明了它们,我特别感兴趣的是理解为什么不同的声明会导致使用for
循环的不同输出。
6条答案
按热度按时间kcwpcxri1#
这只是ES6的语法糖吗?
不,这不仅仅是语法上的糖衣炮弹,血淋淋的细节隐藏在§ 13.6.3.9
CreatePerIterationEnvironment
中。这是怎么回事?
如果在
for
语句中使用let
关键字,它将检查绑定了哪些名称,然后您的循环语句
for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i));
将反糖转换为简单的而
for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i));
对复杂得多的h7appiyu2#
我发现探索ES6书中的解释是最好的:
在for循环头中var声明一个变量会为该变量创建一个绑定(存储空间):
这三个arrow函数体中的每个i都指向同一个绑定,这就是为什么它们都返回相同的值。
如果让-声明一个变量,则会为每个循环迭代创建一个新绑定:
这一次,每个i引用一个特定迭代的绑定,并保留当时的当前值,因此,每个arrow函数返回不同的值。
kgsdhlau3#
let
引入了块作用域和等价绑定,就像函数创建一个带闭包的作用域一样。我相信规范的相关章节是13.2.1,其中的注解提到let
声明是LexicalBinding的一部分,并且都存在于词法环境中。13.2.2节声明var
声明附加到VariableEnvironment,而不是词法绑定。MDN的解释也支持这一点,指出:
它通过在单个代码块的词法作用域中绑定零个或多个变量来工作
这表明变量被绑定到块,块在每次迭代时都需要新的LexicalBinding(我相信,在这一点上不是100%),而不是周围的LexicalEnvironment或VariableEnvironment,后者在调用期间是恒定的。
简而言之,当使用
let
时,闭包在循环体上,变量每次都不一样,所以必须重新捕获;当使用var
时,变量在周围的函数上,所以不需要重新关闭,每次迭代都传递相同的引用。调整示例以在浏览器中运行:
当然显示了后者打印每个值。如果你看看巴别塔是如何传递这个的,它会产生:
假设巴别塔是相当一致的,这符合我对规范的解释。
tf7tbtn24#
最近我也对这个问题感到困惑,根据以上回答,我的理解是:
相当于
3wabscal5#
让我们看一下采访中主要询问的setTimeout中的“let”和“var”。
让我们详细看看这段代码是如何在javascript编译器中执行的。由于函数作用域,“var”的答案是“222”,而“let”的答案是“012”,因为它是块作用域。
现在让我们看看当它编译为“var”时它看起来是什么样的细节。(这比在音频或视频中解释代码有点困难,但我会尽我所能给予你。)
代码最终执行后,它将打印所有console.log,其中“i”的值为6。因此,最终输出为:222
In“let i”将在每个作用域中声明。这里要注意的导入点是**“i”将从前一个作用域中获取值**而不是从声明中获取值。(下面的代码只是一个例子,说明它在编译器中的样子,尝试它不会起作用)
因此,它将根据块范围打印“012”值。
dddzy1tm6#
Let是一个块作用域,在for循环内部声明的Var,甚至可以在for循环外部访问,因为var只是函数作用域,你不能从外部访问函数内部定义的var,每次迭代都会创建一个新的Let,但是因为var是函数作用域,并且可以在for循环外部使用,所以它就变成了全局变量,每次迭代都会更新相同的var变量。