📌  相关文章
📜  for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 1000 + i); } - Javascript (1)

📅  最后修改于: 2023-12-03 14:41:17.953000             🧑  作者: Mango

JavaScript中的setTimeout函数

在JavaScript中,我们经常使用setTimeout函数来延迟代码的执行。setTimeout会在指定的时间对象结束后,将传入的回调函数放入任务队列中,等待执行。

代码示例

下面是使用setTimeout来延迟一秒输出数字的代码:

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

代码分析:

  1. for循环从0循环到2,每次循环会执行一次setTimeout。
  2. 每次setTimeout会将一个匿名函数作为回调函数传入,该函数负责输出i的值。
  3. setTimeout的第二个参数是延迟的时间,第一个setTimeout延迟1秒,第二个为2秒,第三个为3秒。

这个代码看上去很简单,但是运行起来却会出现一些意外的结果。

输出结果

如果你运行上面的代码,你会发现它输出的是三个3,而不是0、1、2这三个数字,这是因为JavaScript中的变量作用域问题。

当我们使用var声明一个变量时,它的作用域是从声明位置开始,直到函数体结束。在上面的代码中,每个setTimeout函数内的回调函数都是在for循环结束后执行的。所以当回调函数被执行时,变量i已经是3了。

为了解决这个问题,我们可以使用闭包来保护变量i的值。

改进代码

下面是一个改进后的代码,它使用闭包来保存每个回调函数中的i的值。

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

代码分析:

  1. 我们将setTimeout的匿名函数用一个立即执行函数包裹起来。
  2. 立即执行函数会接受一个参数i,并在函数内创建一个新的作用域。
  3. 我们把i传递给立即执行函数,使它能够在闭包内使用。

这个方法虽然可以解决变量作用域问题,但是代码的可读性不如原来的方法。因此,我们可以使用ES6中的let关键字。

使用let关键字

ES6中的let关键字会将变量的作用域限制在块级作用域内。下面是一个使用let关键字的代码示例。

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

代码分析:

  1. 我们使用let声明了变量i,使它的作用域仅限于循环内部。
  2. 每个回调函数会在事件循环中被调度,因此每个回调函数都会在不同的块级作用域内执行。
  3. 由于每个回调函数都有自己独立的作用域,因此它们都能够访问它们各自循环迭代的唯一的变量i。

这种方法是比较简洁明了的,并且它避免了使用闭包的复杂性。因此,在ES6中,我们应该尽可能使用let来声明变量。