奇怪的JavaScript代码执行顺序

时间:2015-03-18 14:19:01

标签: javascript

好吧,首先是堆栈溢出问题。我希望我做得对。

我正在尝试运行此代码:

    for(var i = 1; i < 17; i++){
    console.log("for loop runs. i is " + i);
    setTimeout(function(){
        console.log("setTimeout runs. i is " + i);
        if(i < 3){
            $( ".example1" ).append( i );
            $( ".example2" ).append( i );
            $( ".example3" ).append( i );
            $( ".example4" ).append( i );
            $( ".example5" ).append( i );
        }
        else if(i<5){
            $( ".example1" ).append( i );
            $( ".example2" ).append( i );
            $( ".example3" ).append( i );
            $( ".example5" ).append( i );
        }
         else if(i<11){
            $( ".example1" ).append( i );
            $( ".example2" ).append( i );
            $( ".example3" ).append( i );
        }
        else if(i<15){
            $( ".example1" ).append( i );
            $( ".example3" ).append( i );           
        }
        else if(i<17){
            $( ".example1" ).append( i );
        }
    },200);
} //end for loop

我在控制台中收到了这个输出:

    for loop runs. i is 2
    for loop runs. i is 3
    for loop runs. i is 4
    for loop runs. i is 5
    for loop runs. i is 6
    for loop runs. i is 7
    for loop runs. i is 8
    for loop runs. i is 9
    for loop runs. i is 10
    for loop runs. i is 11
    for loop runs. i is 12
    for loop runs. i is 13
    for loop runs. i is 14
    for loop runs. i is 15
    for loop runs. i is 16
    (16) setTimeout runs. i is 17 // <---- runs 16 times

因此for循环运行并增加到17,然后setTimeout运行16次。我不明白这里发生了什么。

3 个答案:

答案 0 :(得分:1)

解决方案: setTimeout执行

时变量没有预期值

可变关闭问题。 IIFE救援:

for(var i = 1; i < 17; i++){
  console.log("for loop runs. i is " + i);
  (function (ii) {
    setTimeout(function(){
      console.log("setTimeout runs. ii is " + ii);
      // the rest of your code goes here
    },200);
  }(i));
}

说明:

您获得价值的原因&#34; 17&#34;对于i,因为在setTimeout执行时,i已被赋值为&#34; 17&#34;使用for循环的最后一次迭代。 (i 已将设置为&#34; 17&#34;以便循环终止。)当您将i传递给{{1你提供setTimeout而不是 i的当前值的功能。

变量范围由它们在执行该函数时实例化的函数(i&d; d)决定。准备好var循环后i(自动)var。那么什么&#34;功能&#34; for循环是否存在? for全球范围。这是window的范围。这就是i在触发时获取setTimeout值的地方。

因此,为了解决这个问题,我们需要在函数中实例化一个变量并执行该函数,以便在执行程序时锁定该变量的值。

从概念上讲,首先,使用一个或多个参数编写一个函数:

i
执行此函数时,

function (x, y, z) { // stuff here }; xy会在此函数范围内获取var。

其次,执行函数,传递必要的参数:

z

function (x, y, z) { // stuff here }(a, b, c); ab已经存在,可能在全局范围内,现在您将其当前值传递给此函数作为参数。

第三,将函数包装在括号中,以便JavaScript解释器将其视为function expression and not a function declaration

c

顺便说一下,这也有效:

(function (x, y, z) {
  // stuff here
}(a, b, c));

...与!function (x, y, z) { // stuff here }(a, b, c); ~+一样。你只需要在行的开头使用一些良性操作符来将JS从它的函数声明狂热中剔除。

有点像-循环,函数执行时函数参数变量会自动for,但在这种情况下,范围在此函数本身内。因此,在该代码行中执行时var将传递给IIFE函数内的新变量i并锁定到该函数范围。现在,当ii最终准备好执行时,它会查找该函数范围,而不是 global ,以获取该值。

答案 1 :(得分:0)

阅读有关javascript中的闭包的更多信息。这里发生的是在for循环内创建多个函数(闭包),共享相同的环境。到调用setTimeout函数时,循环完成迭代,i变量总是设置为17。

了解更多here

答案 2 :(得分:0)

for循环运行16次,i = 1到16(如预期的那样)。 你的setTimeout也会运行16次,因为它在循环中同样运行。

因为你的setTimeout函数在200毫秒后运行,但循环的16次迭代在该时间的一小部分内运行,所有setTimeouts在循环结束后很长时间运行,当时我已经设置为17。