微信扫一扫打赏支持

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其他资料来加深印象(百度是基本上搜不到什么有用的文章的)。

 
 
 
posted @ 2020-03-09 20:14  范仁义  阅读(273)  评论(0)    收藏  举报