首页 > 代码库 > JavaScript---循环与闭包

JavaScript---循环与闭包

  循环与闭包

    先看一个demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        for(var i = 0; i <= 5; i++){
            
                setTimeout(function timer(){
                    console.log(i);
                }, i*1000);
                
        }

    </script>
</body>
</html>

    结果会打印出什么? 相信大家都知道 答案是 6个6; 我们本意是打印出:0,1,2,3,4,5  但结果却是6个6, 为什么?首先解释一下为什么打印出6:这里涉及到延时函数的执行机制,虽然延时函数表明是i秒后执行,而i秒是相对与所有可执行代码执行完那一刻开始计时的,也就是等for循环结束之后,延时函数才开始执行。但此时 i= 6;所以会打印出6. 现在在解释一下为什么是6个6:很简单,for循环每执行一次就创建出一个延时函数,执行了6次,就会有6个延时函数。这6个延时函数共享一个作用域(本例中全局作用域) i又累加到6,所以会打印出6个6!!

    分析完整个过程,你知道问题出现在哪里吗?   有2个问题  1:每执行一次for循环,应该执行一次延时函数,但并没有。2:所有的延时函数共享一个作用域。

知道问题的地方,那就很好解决了:每循环一次,创建一个作用域 。 关键是如何实现“每循环一次,创建一个作用域”, 其实很简单,每循环一次,就立即执行一次延时函数,立即执行函数(IIFE)就派上用场了 它的原理很“粗暴”:每个延时函数都会将IIFE在每次迭代中创建的作用域 封闭起来。ok,修改一下我们的代码:

  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        for(var i = 0; i <= 5; i++){
            (function(){
                setTimeout(function timer(){
                    console.log(i);
                }, i*1000);
            })();
                
        }

    </script>
</body>
</html>

  在chrome中显示:

技术分享

 

  咦??? 怎么还是6个6!!!  问题出在哪里?  其实很简单,仔细看一下,我们的IIFE只是一个什么都没有的空作用域(i是全局变量,处于全局作用域中) 它需要有自己的变量,用来在每个迭代中存储i的值   再来改进一下代码:

  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        for(var i = 0; i <= 5; i++){
            (function(){
                var j = i;
                setTimeout(function timer(){
                    console.log(j);
                }, j*1000);
            })();
                
        }

    </script>
</body>
</html>

 

  在chrome中显示:

技术分享

  把i值存进来,就可以实现 我们的预期了。到现在为止,代码已经是没有错误的了。但是,还可以优化:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        for(var i = 0; i <= 5; i++){
            (function(i){
                setTimeout(function timer(){
                    console.log(i);
                }, i*1000);
                
            })(i)
        }



    </script>
</body>
</html>

  把i当作参数传进去,我们的代码就更完美了。

                                        2017-3-23  0:19

JavaScript---循环与闭包