无法理解闭包细节 - for循环中的setTimeout

时间:2017-09-07 15:17:14

标签: javascript

我无法理解为什么下面的第一个代码段快速执行所有五个console.log,而第二个代码段执行console.log一秒钟

我知道需要一个闭包(也就是一个函数+声明它的范围)" close"超过i循环中不同点的for值。

我理解在第一个片段中,会立即调用最里面的函数,而不会立即调用第二个片段的最内层函数。

然而,我并不清楚实际发生了什么。

有些问题:

  • 在第一个片段中,最内层的函数是在单独的刻度上执行的吗?
  • 在第一个片段中,为什么最里面的函数执行得如此之快?
  • 在第二个片段中,为什么最里面的函数会执行一秒钟的间隔?



for (var i = 1; i <= 5; i++) {
  setTimeout(
    (function (x) {
      (function () {
        console.log(x)
      })(i)
    })(i),
    i * 1000
  )
}
&#13;
&#13;
&#13;

&#13;
&#13;
for (var i = 1; i <= 5; i++) {
  setTimeout(
    (function (x) {
      return (function () {
        console.log(x)
      })
    })(i),
    i * 1000
  )
}
&#13;
&#13;
&#13;

注意:我确实理解使用let会让这更容易,但我试图在闭包方面理解这一点。

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

3 个答案:

答案 0 :(得分:2)

这里的差异不是关于闭包的工作方式,而是关于setTimeoute()如何工作的更多信息。 setTimeout将在传入时间后的某个时间调用传入的函数。所以你传递一个函数:

setTimeout(someFunction, 1000)

在1000毫秒左右的时间内执行someFunction()

在第一个示例中,您通过立即调用setTimout来执行someFunction()有机会之前的函数。当setTimeout到达它时,它没有调用函数 - 只是你已经调用的函数的返回值。在当前刻度上同步调用这些函数。

您可以将其视为传递回调。如果您将回调传递给类似someFunction(null, cb)的函数,则可以稍后使用cb()执行回调,但如果您传递someFunction(null, cb()),则会收到cb的返回值,而不是cb本身。如果这是一个函数,它会调用它。

在第二个示例中,您立即执行外部函数,但返回setTimeout稍后可以调用的函数。这就是它的作用,这就是为什么它按预期工作的原因。

答案 1 :(得分:2)

这与封闭的机制无关。这是100%由函数的工作原理引起的。

现在,让我们分开两个不同的功能。为清楚起见,我将完全删除for循环:

1:

var i = 1; // We simply hardcode `i` for this demo.

function a (x) {
    (function(){
      console.log(x);
    })(i); // immediately call this function

    // THIS IS THE MOST IMPORTANT PART OF THE CODE
    // Yes, this space with no code at all at the end of
    // this function is the MOST IMPORTANT part of the code.
    // This absence of code represents "return undefined".
    // All functions that return nothing returns undefined.
}

setTimeout(a(i),i * 1000);

注意:请记住a()会返回undefined。所以setTimeout实际上是:

setTimeout(undefined,1000);

&#34;帮助&#34;,如果您将未定义传递给setTimeout,它将优雅地接受它并且不会产生任何错误。

在调用console.log时,您也可以直接致电a()

现在让我们看看其他代码:

2:

var i = 1;

function b (x) {
  return (function () {
    console.log(x)
  }) // <--- note you are not calling the inner function at all
}

setTimeout(b(i), i * 1000);

请注意,由于b()返回一个函数setTimeout,将在1秒后调用b()(调用console.log的函数)返回的函数。

答案 2 :(得分:1)

这很简单,但很棘手:)

在第一个示例中,您创建并执行匿名函数。然后将undefined返回到setTimeout。 setTimeout将不执行任何操作。这就是为什么它快速执行。

在第二个示例中,您创建并执行一个匿名函数,该函数创建另一个匿名函数并将其返回到setTimeout。然后setTimeout将执行它。

见我的评论:

for (var i = 1; i <= 5; i++) {
  setTimeout(
    (function (x) {
      (function () {
        console.log(x)
      })(i)  -> create and EXECUTE anonymous function (execute it right away)
    })(i), -> create and execute anonymous function. returns undefined
    i * 1000
  )
}


for (var i = 1; i <= 5; i++) {
  setTimeout(
    (function (x) {
      return (function () {
        console.log(x)
      })
    })(i), -> create and execute anonymous function. returns a new function
    i * 1000
  )
}
相关问题