小能豆

JavaScript 循环内的闭包——简单实用的例子

javascript

JavaScript 循环内的闭包——简单实用的例子


阅读 34

收藏
2024-09-02

共1个答案

小能豆

在 JavaScript 中,闭包指的是函数能够“记住”它所在的词法环境,即使在函数执行上下文(作用域)已经结束的情况下,依然可以访问到该作用域中的变量。闭包在循环中非常常见,特别是在处理异步操作时。

闭包在循环中的常见问题

在 JavaScript 中,闭包在循环中使用时可能会导致一些意想不到的行为。例如,如果你希望在循环内部定义的函数使用当前的循环变量,那么你可能会遇到变量共享的问题。来看一个例子:

问题示例

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

输出:

4
4
4

在这个例子中,setTimeout 回调函数在 1 秒后执行,循环已经结束,此时 i 的值为 4,因此每个回调都会输出 4

解决方案:使用闭包

为了使每个回调函数输出当前的 i 值,我们可以使用闭包创建一个新的词法环境,使得每个 i 都有其独立的作用域:

for (var i = 1; i <= 3; i++) {
  (function(currentI) {
    setTimeout(function() {
      console.log(currentI);
    }, 1000);
  })(i);
}

输出:

1
2
3
解释:
  • 我们用一个立即调用的函数表达式(IIFE)创建了一个闭包。
  • currentI 是 IIFE 的参数,在调用时传入当前的 i
  • currentI 被“冻结”在这个新的词法作用域内,不会受后续循环的影响。

更简单的方法:let 声明

ES6 引入的 let 关键字,可以更自然地解决这个问题,因为 let 在块作用域(block scope)中声明变量:

for (let i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

输出:

1
2
3
解释:
  • 使用 let 声明的 i 在每次循环迭代中都有自己的作用域。因此,每个 setTimeout 回调都会引用当时的 i 值。

总结

  • 使用闭包(通过 IIFE)或 let 关键字解决循环内闭包变量共享的问题。
  • let 更加直观和简单,建议在支持 ES6 的环境中优先使用。
2024-09-02