Return 带 let 变量的循环内存储函数的值评估

Return value evaluation of a stored function inside a loop with let variable

据我了解,如果 for 循环的循环变量是用 var 定义的,那么对该变量的任何更改都会全局应用。例如:

var printNumTwo;
for (var i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());

在上面的代码中,3 将打印到控制台中。因为在最后一次迭代中变量 i 将等于 3。因此当调用 printNumTwo 时,将返回更新 i。 但是,如果我使用 let 则情况并非如此,并且会发生以下行为:

let printNumTwo;
for (let i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
  }
}
console.log(printNumTwo());

以上代码将打印 2.

let printNumTwo;
for (let i = 0; i < 3; i++) {
  if (i === 2) {
    printNumTwo = function() {
      return i;
    };
    i = 3;
  }
}
console.log(printNumTwo());

然而,上面的代码打印了 3。 这是什么原因?

来源:The first two code blocks are from here.

however, the above code prints 3. What is the reason for this?

因为您将 3 分配给 i 变量,printNumTwo 关闭了。在创建 printNumTwo 之后发生赋值并不重要,只是它是 printNumTwo 正在使用的变量。

varletfor 循环中的区别在于,为循环体创建了一个 new 变量每个循环迭代 let。但是你在循环体内分配给那个变量,所以函数关闭 (printNumTwo) 它稍后在你调用它时看到那个值。

完全是这样的:

function create(i) {
    // This function closes over `i`
    const fn = function() {
        return i;
    };

    // This modifies the `i` it closes over
    ++i;

    return fn;
}
const printValue1 = create(1);
console.log(printValue1()); // 2 (1 + 1)

const printValue27 = create(27);
console.log(printValue27()); // 28 (27 + 1)


在回答对该问题的评论时,您说:

same thing also happens in the second code block (i++ in the update section in for loop ) but the answer is 2

啊!现在我明白为什么会有误会了。你是对的 i 有更新——但 printNumTwo 关闭的不是 i

for 的更新部分在下一次迭代开始时对 new 变量执行,而不是在迭代的那个上执行刚刚在迭代结束时完成。它这样做:

  • 创建一个新的迭代变量(我们称之为iNew
  • 将旧迭代变量(我们称它为iOld)的值赋给新变量(iNew = iOld
  • 运行 更新代码使用 iNew (iNew++)

这就是为什么 i(旧的)仍然 2 的原因;这是新的 3.

在第一个示例中,var 作用域 i 到函数,因此在循环结束时 i++i2 更改为3 循环停止。 只有 i 现在是 3printNumTwo returns i3.

在第二个例子中,let 作用域 i 到块,因此分配给 printNumTwo 的函数关闭它。 last 循环主体运行的时间,i2printNumTwo returns i2。 (使用值 3 创建了一个新的 i,但没有创建使用它的函数)。

在第三个例子中,let 范围 i 到块,同样的事情发生了,除了你有 i = 3; 改变 every i3。所以最后创建的函数(以及之前被覆盖的函数)returns i3.

var的范围是函数,let的范围是块。

所以给printNumTwo函数赋值的时候,i的值还是2,跟后面的i怎么样都没有关系。

var 和 let 的行为你可以从这个例子中理解。

for(var i=0; i<3; i++){
    setTimeout(() => console.log('var', i))
}

for(let i=0; i < 3; i++){
    setTimeout(() => console.log('let',i))
}