Javascript 使用for循环解释'let'和块作用域
我知道让防止重复声明是很好的Javascript 使用for循环解释'let'和块作用域,javascript,ecmascript-6,Javascript,Ecmascript 6,我知道让防止重复声明是很好的 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++) { proces
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)) }
//打印“10”10次
对于(var i=0;i<10;i++){process.nextTick(=>console.log(i))}
//打印“0”到“9”
对于(设i=0;i<10;i++){process.nextTick(=>console.log(i))}
为什么在这种情况下使用会让起作用?在我的想象中,即使只有一个块是可见的,for
实际上为每个迭代创建一个单独的块,let
声明是在该块内部完成的。。。但是只有一个let
声明来初始化该值。这只是ES6的语法糖吗?这是怎么回事
我理解var
和let
之间的区别,并在上面进行了说明。我特别感兴趣的是理解为什么不同的声明使用for
循环会产生不同的输出。让
引入块作用域和等效绑定,就像函数使用闭包创建作用域一样。我相信规范的相关部分是,注释中提到let
声明是词汇绑定的一部分,它们都存在于词汇环境中。第节说明var
声明附加到可变环境,而不是字典绑定
委员会也支持这一点,指出:
它通过在单个代码块的词法范围内绑定零个或多个变量来工作
建议变量绑定到块,这会改变每次迭代,需要一个新的LexicalBinding(我相信,在这一点上不是100%),而不是周围的lexicalEnvironment或VariableEnvironment,它在调用期间是恒定的
简而言之,当使用let
时,闭包位于循环体,变量每次都不同,因此必须再次捕获它。当使用var
时,变量位于周围的函数中,因此不需要重新关闭,并且将相同的引用传递给每个迭代
调整示例以在浏览器中运行:
// prints '10' 10 times
for (var i = 0; i < 10; i++) {
setTimeout(_ => console.log('var', i), 0);
}
// prints '0' through '9'
for (let i = 0; i < 10; i++) {
setTimeout(_ => console.log('let', i), 0);
}
//打印“10”10次
对于(变量i=0;i<10;i++){
setTimeout(=>console.log('var',i),0);
}
//打印“0”到“9”
for(设i=0;i<10;i++){
setTimeout(=>console.log('let',i),0);
}
当然显示后者打印每个值。如果你看一下巴别塔是如何传输的,它会产生:
for(变量i=0;i<10;i++){
setTimeout(函数41;{
返回控制台日志(i);
}, 0);
}
var _loop=函数(_i){
setTimeout(函数41;{
返回控制台日志(_i);
}, 0);
};
//打印“0”到“9”
对于(var_i=0;_i<10;_i++){
_环(_i);
}
这只是ES6的语法糖吗
不,不仅仅是语法上的糖。血淋淋的细节被掩盖了
这是怎么回事
如果在for
语句中使用let
关键字,它将检查绑定的名称,然后
- 使用这些名称创建一个新的词汇环境,用于a)初始化器表达式b)每次迭代(在计算增量表达式之前)
- 将具有这些名称的所有变量的值从一个环境复制到下一个环境
(vari=0;i<10;i++)进程的循环语句代码>去糖到一个简单的
// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
…
//当大括号不引入块时忽略大括号
var i;
i=0;
如果(i<10)
process.nextTick(=>console.log(i))
i++;
如果(i<10)
process.nextTick(=>console.log(i))
i++;
…
而for(让i=0;i<10;i++)process.nextTick(=>console.log(i))代码>将“desugar”转换为更复杂的
// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
i = 0;
__status = {i};
}
{ let {i} = __status;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
…
//使用大括号显式表示块范围,
//使用缩进控制流
{让我来;
i=0;
__状态={i};
}
{let{i}={u状态;
如果(i<10)
process.nextTick(=>console.log(i))
__状态={i};
}{let{i}={u状态;
i++;
如果(i<10)
process.nextTick(=>console.log(i))
__状态={i};
}{let{i}={u状态;
i++;
…
我找到了最好的:
在for循环的头部声明变量的var将创建一个
该变量的绑定(存储空间):
const arr = [];
for (var i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [3,3,3]
const arr=[];
对于(变量i=0;i<3;i++){
arr.push(()=>i);
}
arr.map(x=>x());//[3,3,3]
三个箭头函数的主体中的每个i都引用相同的函数
绑定,这就是它们都返回相同值的原因
如果让您声明一个变量,则会为每个循环创建一个新绑定
迭代:
const arr = [];
for (let i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [0,1,2]
const arr=[];
for(设i=0;i<3;i++){
arr.push(()=>i);
}
arr.map(x=>x());//[0,1,2]
这一次,每个i都引用一个特定迭代和
保留当时的当前值。因此
arrow函数返回一个不同的值
在你的问题中你几乎已经说过了,每次循环迭代时,let
基本上都会重新计算。我不知道我会称之为语法糖,这只是循环是如何定义的。See的可能重复。阅读这两篇文章:,我在这里是否正确:let I=100;setTimeout(function(){console.log(I)},I);//“100”100毫秒后,我们还可以使用var i=100?@TinyGiant大多数浏览器无法运行第一个代码段,因为它们往往不支持let和arrow函数。我已经阅读了好几遍规范——我仍然不认为我完全理解,但查看Babel的输出会有所帮助。E