JavaScript:了解 for 循环中的 let 作用域
JavaScript: Understanding let scope inside for loop
请考虑以下代码段-
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
在这种情况下,setTimeout
中的日志将根据 for 循环的每次迭代包含变量 i
的值,即日志如下
1
2
3
4
5
为此,我在 Internet 上阅读了解释,例如 - let
为每个循环创建一个变量声明,它是块级声明。所以基本上它在 { }
.
内创建了一个范围
但是我对这个说法有点困惑。如果let
为每个循环创建一个变量声明,它不会总是按照循环初始化语句let i=1
被初始化为1
吗?
此外,变量 i
在循环块外部声明、初始化和递增,即 for
循环语句中的花括号。那么,在每次迭代中,不是递增并使用相同的变量 i
吗? let
究竟是如何为每个循环创建变量声明并具有上一次迭代的值的?
这样的 for 循环:
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
和这样做是一样的:
{
let i = 1;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 2;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 3;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 4;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 5;
setTimeout(function() {
console.log(i);
}, 100);
}
变量在 for 循环的范围内声明和赋值五次,每个实例都与其他实例完全分开。
一般说明
当您在 for
循环结构中使用 let
时,就像您展示的那样,会为每次循环调用创建一个新变量 i
,该变量的范围仅限于块循环的(在循环外不可访问)。
循环的第一次迭代从 for
循环初始值设定项(在您的示例中为 i = 1
)获取其值。每次循环迭代创建的其他新 i
变量从上一次循环调用的 i
获取它们的值,而不是从 i = 1
获取它们的值,这就是为什么它们没有全部初始化至 1
.
因此,每次循环都会有一个新变量 i
与所有其他变量分开,每个新变量都用前一个变量的值初始化,然后由 i++
在 for
循环声明中。
对于你的 ES6 代码:
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
在 ES5 中,这将是一个本质上等效的结构。如果你真的研究这个,它可以为你上面的 ES6 代码中实际发生的事情提供非常有用的信息:
(function() {
for (var i = 1; i <= 5; i++) {
i = (function(j) {
setTimeout(function(){
console.log(j);
},100);
return j;
})(i);
}
})();
需要两个IIFE(立即调用的函数表达式)来模拟这个。外层隔离了 var i
,这样它就不会从 for
循环中泄漏出来,而内层为 for
循环的每次调用提供了一个单独的范围和单独的变量。 return j
和 i = (function(j) {...})(i)
是为了显示循环的下一次迭代如何受到循环变量修改的影响。
希望这能说明 let
对于 ES6 中的 for
循环有多么有用,以及当您 need/want 此功能时它会替换多少其他代码。
现在回答您的具体问题
For this, I have read explanations over the Internet like - let creates a variable declaration for each loop which is block level declaration. So basically it creates a scope within { }
let
定义具有块作用域的变量(不像 var
那样的函数作用域)。而且,描述 for
循环的 {
和 }
确实定义了一个范围。
Also, variable i is declared, initialized and incremented outside the loop block i.e. curly braces in the for loop statement.
嗯,不完全是。 for
循环是关于如何初始化为循环的第一次调用创建的第一个 i
的指令。 let i = 1
出现在循环之外的事实看起来确实有点令人困惑,但它实际上只是一个指令,说明当它为循环的第一次调用创建第一个 i
变量时要做什么。第一个 i
变量实际上并不存在于循环范围之外。
So, in each iteration isn’t the same variable i is incremented and utilized?
没有。当 ES6 遇到定义为 let
的 for
循环时,它会为循环的每次迭代创建一个新变量。
How exactly does let creates a variable declaration for each loop and has value of previous iteration?
这不是 let
做的。这是 ES6+ JS 解释器中的 for
循环逻辑。这是 for
循环的一种特殊行为,该循环具有使用 let
声明的索引初始值设定项。因此,它是 let
和 for
的组合行为,但真正的逻辑在于解释器执行 for
循环的方式。
修改循环变量时的特例
let
在for
循环中也有一个特例。如果您在循环中分配 i
的值,它将更改 i
的特定值,并且还会影响下一次迭代的 i
的值。这有点特殊,但它允许您仍然在循环中操作 i
的值。
for(let i = 1; i <= 5; i++) {
let j = i;
setTimeout(function(){
console.log(j);
},100);
if (i === 2) {
i++; // bump the loop increment to skip the 3 value
}
}
这将创建输出:
1
2
4
5
所以,这会跳过循环的 3
迭代,因为当 i === 2
时,我们将其递增到 3,然后 for
循环执行其 i++
迭代并且将它提高到 4
,有效地跳过 3
迭代。
在javascript中你可以通过var或let来定义一个变量。定义为 let 的变量不 refined.Lets 考虑示例代码片段
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript let</h2>
<p id="demo"></p>
<script>
var i = 3;
for (var i = 0; i < 10; i++) {
// some statements
document.getElementById("demo").innerHTML += i;
}
document.getElementById("demo").innerHTML += i;
</script>
</body>
</html>
在此代码中,输出将是 --> 012345678910
相比之下,循环中带let的变量不会在循环外重新声明变量。
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript let</h2>
<p id="demo"></p>
<script>
let i = 5;
for (let i = 0; i < 10; i++) {
// some statements
document.getElementById("demo").innerHTML += i;
}
document.getElementById("demo").innerHTML += i;
</script>
</body>
</html>
以上代码的输出将是 --> 01234567895
这里循环外的变量保持不变,执行内层循环,循环结束后,全局变量的值仍然是5。基本上是按照作用域来分隔变量。
请考虑以下代码段-
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
在这种情况下,setTimeout
中的日志将根据 for 循环的每次迭代包含变量 i
的值,即日志如下
1
2
3
4
5
为此,我在 Internet 上阅读了解释,例如 - let
为每个循环创建一个变量声明,它是块级声明。所以基本上它在 { }
.
但是我对这个说法有点困惑。如果let
为每个循环创建一个变量声明,它不会总是按照循环初始化语句let i=1
被初始化为1
吗?
此外,变量 i
在循环块外部声明、初始化和递增,即 for
循环语句中的花括号。那么,在每次迭代中,不是递增并使用相同的变量 i
吗? let
究竟是如何为每个循环创建变量声明并具有上一次迭代的值的?
这样的 for 循环:
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
和这样做是一样的:
{
let i = 1;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 2;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 3;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 4;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 5;
setTimeout(function() {
console.log(i);
}, 100);
}
变量在 for 循环的范围内声明和赋值五次,每个实例都与其他实例完全分开。
一般说明
当您在 for
循环结构中使用 let
时,就像您展示的那样,会为每次循环调用创建一个新变量 i
,该变量的范围仅限于块循环的(在循环外不可访问)。
循环的第一次迭代从 for
循环初始值设定项(在您的示例中为 i = 1
)获取其值。每次循环迭代创建的其他新 i
变量从上一次循环调用的 i
获取它们的值,而不是从 i = 1
获取它们的值,这就是为什么它们没有全部初始化至 1
.
因此,每次循环都会有一个新变量 i
与所有其他变量分开,每个新变量都用前一个变量的值初始化,然后由 i++
在 for
循环声明中。
对于你的 ES6 代码:
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
在 ES5 中,这将是一个本质上等效的结构。如果你真的研究这个,它可以为你上面的 ES6 代码中实际发生的事情提供非常有用的信息:
(function() {
for (var i = 1; i <= 5; i++) {
i = (function(j) {
setTimeout(function(){
console.log(j);
},100);
return j;
})(i);
}
})();
需要两个IIFE(立即调用的函数表达式)来模拟这个。外层隔离了 var i
,这样它就不会从 for
循环中泄漏出来,而内层为 for
循环的每次调用提供了一个单独的范围和单独的变量。 return j
和 i = (function(j) {...})(i)
是为了显示循环的下一次迭代如何受到循环变量修改的影响。
希望这能说明 let
对于 ES6 中的 for
循环有多么有用,以及当您 need/want 此功能时它会替换多少其他代码。
现在回答您的具体问题
For this, I have read explanations over the Internet like - let creates a variable declaration for each loop which is block level declaration. So basically it creates a scope within { }
let
定义具有块作用域的变量(不像 var
那样的函数作用域)。而且,描述 for
循环的 {
和 }
确实定义了一个范围。
Also, variable i is declared, initialized and incremented outside the loop block i.e. curly braces in the for loop statement.
嗯,不完全是。 for
循环是关于如何初始化为循环的第一次调用创建的第一个 i
的指令。 let i = 1
出现在循环之外的事实看起来确实有点令人困惑,但它实际上只是一个指令,说明当它为循环的第一次调用创建第一个 i
变量时要做什么。第一个 i
变量实际上并不存在于循环范围之外。
So, in each iteration isn’t the same variable i is incremented and utilized?
没有。当 ES6 遇到定义为 let
的 for
循环时,它会为循环的每次迭代创建一个新变量。
How exactly does let creates a variable declaration for each loop and has value of previous iteration?
这不是 let
做的。这是 ES6+ JS 解释器中的 for
循环逻辑。这是 for
循环的一种特殊行为,该循环具有使用 let
声明的索引初始值设定项。因此,它是 let
和 for
的组合行为,但真正的逻辑在于解释器执行 for
循环的方式。
修改循环变量时的特例
let
在for
循环中也有一个特例。如果您在循环中分配 i
的值,它将更改 i
的特定值,并且还会影响下一次迭代的 i
的值。这有点特殊,但它允许您仍然在循环中操作 i
的值。
for(let i = 1; i <= 5; i++) {
let j = i;
setTimeout(function(){
console.log(j);
},100);
if (i === 2) {
i++; // bump the loop increment to skip the 3 value
}
}
这将创建输出:
1
2
4
5
所以,这会跳过循环的 3
迭代,因为当 i === 2
时,我们将其递增到 3,然后 for
循环执行其 i++
迭代并且将它提高到 4
,有效地跳过 3
迭代。
在javascript中你可以通过var或let来定义一个变量。定义为 let 的变量不 refined.Lets 考虑示例代码片段
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript let</h2>
<p id="demo"></p>
<script>
var i = 3;
for (var i = 0; i < 10; i++) {
// some statements
document.getElementById("demo").innerHTML += i;
}
document.getElementById("demo").innerHTML += i;
</script>
</body>
</html>
在此代码中,输出将是 --> 012345678910 相比之下,循环中带let的变量不会在循环外重新声明变量。
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript let</h2>
<p id="demo"></p>
<script>
let i = 5;
for (let i = 0; i < 10; i++) {
// some statements
document.getElementById("demo").innerHTML += i;
}
document.getElementById("demo").innerHTML += i;
</script>
</body>
</html>
以上代码的输出将是 --> 01234567895 这里循环外的变量保持不变,执行内层循环,循环结束后,全局变量的值仍然是5。基本上是按照作用域来分隔变量。