• javascript疑难问题---8、闭包在循环中的问题


    javascript疑难问题---8、闭包在循环中的问题

    一、总结

    一句话总结:

    闭包在循环中可能会遇到闭包调用的变量是循环执行完之后的变量,比如演示中的输出结果都是“元素5”,可以通过 【让匿名函数立即执行】 或 【用闭包局部变量常驻内存的特点来保存中间值的方法】 来解决这个问题
        /*
        * 现象:数组里面的匿名函数的执行结果 都是  元素5
        * 原因:匿名函数执行的时候,外层f1()函数已经执行完了,
        * 循环中i的值是5,而匿名函数执行的时候找的i的值就是循环中i的值,
        * 所以也是5
        * */
        // function f1(){
        //     var arr=[];
        //     for(var i=0; i<5; i++){
        //         arr[i]=function () {
        //             return '元素'+i;
        //         };
        //     }
        //     return arr;
        // }
        // // console.log(f1());
        // // console.log(f1()[0]);
        // var arr1=f1();
        // //arr1[0]();
        // console.log(arr1[0]());
        // console.log(arr1[1]());
        // console.log(arr1[2]());
        // console.log(arr1[3]());
        // console.log(arr1[4]());
    
        /*解决方法一:
        * 让匿名函数立即执行
        * */
        // function f1(){
        //     var arr=[];
        //     for(var i=0; i<5; i++){
        //         arr[i]=(function () {
        //             return '元素'+i;
        //         })();
        //     }
        //     return arr;
        // }
        // console.log(f1());
    
        /*
        * 解决方法二:
        * 用闭包局部变量常驻内存的特点来保存中间值,
        * */
        function f1(){
            var arr=[];
            for(var i=0; i<5; i++){
                arr[i]=(function (i) {
                    return function () {
                        return '元素'+i;
                    };
                })(i);
            }
            return arr;
        }
        console.log(f1());
        console.log(f1()[0]);
        var arr1=f1();
        console.log(arr1[0]());
        console.log(arr1[1]());
        console.log(arr1[2]());
        console.log(arr1[3]());
        console.log(arr1[4]());

    二、闭包在循环中的问题

    博客对应课程的视频位置:8、闭包在循环中的问题
    https://www.fanrenyi.com/video/4/160

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>闭包在循环中的问题</title>
     6 </head>
     7 <body>
     8 <!--
     9 
    10 -->
    11 <script>
    12     // function f1(){
    13     //     var arr=[];
    14     //     for(var i=0; i<5; i++){
    15     //         arr[i]='元素'+i;
    16     //     }
    17     //     return arr;
    18     // }
    19     // console.log(f1());
    20 
    21 
    22     /*
    23     * 现象:数组里面的匿名函数的执行结果 都是  元素5
    24     * 原因:匿名函数执行的时候,外层f1()函数已经执行完了,
    25     * 循环中i的值是5,而匿名函数执行的时候找的i的值就是循环中i的值,
    26     * 所以也是5
    27     * */
    28     // function f1(){
    29     //     var arr=[];
    30     //     for(var i=0; i<5; i++){
    31     //         arr[i]=function () {
    32     //             return '元素'+i;
    33     //         };
    34     //     }
    35     //     return arr;
    36     // }
    37     // // console.log(f1());
    38     // // console.log(f1()[0]);
    39     // var arr1=f1();
    40     // //arr1[0]();
    41     // console.log(arr1[0]());
    42     // console.log(arr1[1]());
    43     // console.log(arr1[2]());
    44     // console.log(arr1[3]());
    45     // console.log(arr1[4]());
    46 
    47     /*解决方法一:
    48     * 让匿名函数立即执行
    49     * */
    50     // function f1(){
    51     //     var arr=[];
    52     //     for(var i=0; i<5; i++){
    53     //         arr[i]=(function () {
    54     //             return '元素'+i;
    55     //         })();
    56     //     }
    57     //     return arr;
    58     // }
    59     // console.log(f1());
    60 
    61     /*
    62     * 解决方法二:
    63     * 用闭包局部变量常驻内存的特点来保存中间值,
    64     * */
    65     function f1(){
    66         var arr=[];
    67         for(var i=0; i<5; i++){
    68             arr[i]=(function (i) {
    69                 return function () {
    70                     return '元素'+i;
    71                 };
    72             })(i);
    73         }
    74         return arr;
    75     }
    76     console.log(f1());
    77     console.log(f1()[0]);
    78     var arr1=f1();
    79     console.log(arr1[0]());
    80     console.log(arr1[1]());
    81     console.log(arr1[2]());
    82     console.log(arr1[3]());
    83     console.log(arr1[4]());
    84 </script>
    85 </body>
    86 </html>

    三、参考资料:JS中for循环里面的闭包问题的原因及解决办法

    转自或参考:JS中for循环里面的闭包问题的原因及解决办法
    https://blog.csdn.net/weixin_40333655/article/details/90905514

    我们先看一个正常的for循环,普通函数里面有一个for循环,for循环结束后最终返回结果数组

    function box(){
        var arr = [];
        for(var i=0;i<5;i++){
            arr[i] = i;        
        }
        return arr;
    }
    //alert 输出一个弹出框
    alert(box())                                    //正常情况不需要闭包,就可以达到预期效果,输出结果为一个数组0,1,2,3,4
    

    有时我们需要在for循环里面添加一个匿名函数来实现更多功能,看下面代码

    //循环里面包含闭包函数
    function box(){
        var arr = [];
        for(var i=0;i<5;i++){
            arr[i] = function(){
                return i;                            //由于这个闭包的关系,他是循环完毕之后才返回,最终结果是4++是5
            }                                        //这个匿名函数里面根本没有i这个变量,所以匿名函数会从父级函数中去找i,
        }                                            //当找到这个i的时候,for循环已经循环完毕了,所以最终会返回5
        											//另外循环体内声明了一个匿名函数,而这个匿名函数并没有得到执行,所以arr数组每个元素都是一个匿名函数 function(){return i}
        return arr;
    }
    //alert(box());                                    //执行5次匿名函数本身
    //alert(box()[1]);                   //执行第2个匿名函数本身
    //alert(box().length);                            //最终返回的是一个数组,数组的长度为5
    alert(box()[0]());                                //数组中的第一个数返回的是5,这是为什么?
    

    上面这段代码就形成了一个闭包:
    闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。
    在for循环里面的匿名函数执行 return i 语句的时候,由于匿名函数里面没有i这个变量,所以这个i他要从父级函数中寻找i,而父级函数中的i在for循环中,当找到这个i的时候,是for循环完毕的i,也就是5,所以这个box得到的是一个数组[5,5,5,5,5]。
    解决方案1
    在看解决方案一之前,我们先看一下匿名函数的自我执行:
    匿名函数自我执行的写法是,在函数体外面加一对圆括号,形成一个表达式,在圆括号后面再加一个圆括号,里面可传入参数。
    例如下代码:

    (function(){
        alert('lee');                        //匿名函数自我执行(匿名函数)()
    })();
    

    我们再来看解决方案1:

    function box(){
        var arr = [];
        for(var i=0;i<5;i++){
            arr[i] = (function(num){                    //自我执行,并传参(将匿名函数形成一个表达式)(传递一个参数)
                return num;                            //这里的num写什么都可以                    
            })(i);                                    //这时候这个括号里面的i和上面arr[i]的值是一样的都是取自for循环里面的i                            
        }                                            
        return arr;
    }
    //alert(box());                                
    //alert(box()[1]);
    //alert(box().length);                            
    alert(box()[0]);
    

    通过给匿名函数传参,而传递的这个参数i是每次执行for循环里面的i,每次传递的参数i的值都不一样,匿名函数里面的num接收传递的参数i,所以box()最终输出结果为[0,1,2,3,4]

    解决方案2
    这种方案的原理就是在匿名函数1里面再写入一个匿名函数2,这个匿名函数2需要的num值会在他的父级函数匿名函数1里面去寻找,而匿名函数1里面的num值就是传入的这个参数i,和上面例子中的i是一样的,

    function box(){
        var arr = [];
        for(var i=0;i<5;i++){
            arr[i] = (function(num){
            //num在这里                                    //原理和上面一种方法一样的,所以可以实现闭包                    
                return function(){                        //在这个闭包里面再写一个匿名函数
                    return num;                            
                };                                                                
            })(i)                                                
        }
        return arr;
    }
    //alert(box());                                
    //alert(box()[1]);
    //alert(box().length);
    var b = box();                            
    alert(b[0]());
    alert(box()[0]());
    

    box()最终返回结果[0,1,2,3,4],

    解决方案3
    如果将一个匿名函数自我执行的时候赋值给一个变量,那么这个匿名函数中的圆括号的可以去掉的,看下面代码,

    var tip = function(){                                //这样把匿名函数自我执行的时候赋值给一个变量,那么圆括号是可以去掉的
        alert('lee');
    }();
    

    利用匿名函数的这一特点,我们可以将解决方案1中的代码改进一下:

    function box(){
        var arr = [];
        for(var i=0;i<5;i++){
            arr[i] = function(num){                
                return num;                            
            }(i);                                
        }                                            
        return arr;
    }
    //alert(box());                                
    //alert(box()[1]);
    //alert(box().length);                            
    alert(box()[4]);
    

    匿名函数在执行的时候他本身就传递给了一个变量arr[i],所以匿名函数的圆括号是可以去掉的。
    以上就是几种解决for循环中闭包的办法,当然还有更多办法,大家可自行google或者bing其他资料来加深印象(百度是基本上搜不到什么有用的文章的)。

     
     
     
  • 相关阅读:
    Effective C# 原则12:选择变量初始化而不是赋值语句
    Effective C# 原则20:明辨接口实现和虚函数重载的区别(译)
    Effective C# 原则18:实现标准的处理(Dispose)模式(译)
    Effective C# 原则19:选择定义和实现接口而不是继承(译)
    Is JoyFM goes over? Nope, I believe that JoyFM will stick together with us for a long time.
    Effective C# 原则15:使用using和try/finally来做资源清理(译)
    Effective C# 第二章:.Net资源管理(翻译)
    Effective C# 原则10: 明白GetHashCode()的缺陷(译)
    Effective C# 原则8:确保0对于值类型数据是有效的(翻译)
    Effective C# 原则23:避免返回内部类对象的引用(翻译)
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/12450440.html
Copyright © 2020-2023  润新知