2

我正在研究闭包,但我不明白为什么第二个总是 1 而不是 2、3、4、5。我在这里的理解是,因为每个功能(甚至 iife)都有自己的范围。实际上,每个i变量都被捕获了自己的堆栈,这就是为什么它允许更改ivar使用的值的原因。

同理,setTimeout应分别捕获i具有 1、2、3、4、5(秒)的每个不同变量。但是,它似乎不像图像中看到的那样。你能帮忙吗?

  • 也许它i不是一个自由变量是有帮助的 setTimeout。(不知道有什么关系)

代码

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

一个

4

4 回答 4

0

我也不明白引擎是如何工作的,尽管i它给出了预期的结果。我不希望持续 1 秒。

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

于 2021-01-30T22:45:19.003 回答
0

我认为使用let和 IIFE 会让你失望。使用 IIFE 会为函数参数创建一个新范围。一个鲜为人知的事实是,let在循环中使用会在每次迭代时创建一个新范围。

例如

使用var和 IIFE。

// 'i' is declared here once
for (var i = 1; i <= 5; i++) {

  // 'i' is declared again and takes a new scope every time the function is executed
  (function (i) {
    setTimeout(function () {
      console.log(i) // references the nearest-scoped 'i'
    }, i * 1000)
  })(i)
}

// => 1, 2, 3, 4, 5

使用var但没有 IIFE

// 'i' is declared here once
for (var i = 1; i <= 5; i++) {
    setTimeout(function () {
      console.log(i) // references nearest-scoped 'i'
    }, i * 1000)
}

// => 6, 6, 6, 6, 6

使用let但没有 IIFE

// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {
    setTimeout(function () {
      console.log(i)
    }, i * 1000)
}

// => 1, 2, 3, 4, 5

使用let和 IIFE。这正是你的例子。

// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {

  // 'i' is declared again and takes a new scope every time the function is executed
  (function (i) {
    setTimeout(function () {
      console.log(i) // references the nearest-scoped 'i'
    }, i * 1000)
  })(i)
}

// => 1, 2, 3, 4, 5
于 2021-01-30T21:39:01.527 回答
0

在评论中你说:

我希望 1 秒后 1、2 秒后 2、3 秒后 3、4 秒后 4、5 秒后 5。但是 1,2,3,4,5 每次打印 1 秒后,如图所示

如果您在调用计时器 1 的回调后启动计时器 2 会发生这种情况,但这不是代码所做的。代码立即设置了五个定时器——一个定时器在一秒后显示 1,一个定时器在两秒后显示 2,一个定时器在三秒后显示 3,等等。由于它们都同时启动,你一秒后看到 1,之后一秒看到 2(计划后两秒),之后一秒看到 3(计划后三秒),等等。

为了看到这一点,让我们展示一下我们何时启动每个计时器,以及自上一个计时器触发以来已经过了多长时间。不过,首先,您不需要 IIFE,因为您letfor语句中使用,因此每个循环迭代都有自己的i变量。因此,这是您的原始代码,没有 IIFE,而是在每个计时器启动时添加消息,并显示计时器启动和触发之间的时间间隔;这仍然只会相隔大约一秒钟,而不是按照您所说的去做:

// Still doesn't do what you want, but shows that the IIFE isn't needed
// and when each timer is set up
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
    console.log(`setting timer to show ${i} in ${i * 1000}ms`);
    const started = Date.now();
    setTimeout(function () {
        console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
        previousFired = Date.now();
    }, i * 1000);
}

如果您想等待一秒钟以查看 1,然后再等待两秒钟以查看 2,依此类推,或者在前一个计时器触发之前不要启动下一个计时器,或者稍后安排它们。后者更简单,所以让我们这样做:

let total = 0;
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
    total += i;
    console.log(`setting timer to show ${i} in ${total * 1000}ms`);
    const started = Date.now();
    setTimeout(function () {
        console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
        previousFired = Date.now();
    }, total * 1000);
}

于 2021-02-02T09:51:50.980 回答
-1

您可以处理等待超时 Promise 的异步循环,然后您可以调用外部函数:

function f(i) {
  console.log(i)
}

const runLoop = async () => {
    for(let i = 1; i <= 5; i++){
        await new Promise(resolve => setTimeout(resolve, i * 1000))
        f(i * 1000);
    }
}

runLoop()

于 2021-01-30T19:57:33.210 回答