• JS中for循环里面的闭包问题的原因及解决办法


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

    function box(){

        var arr = [];

        for(var i=0;i<5;i++){

            arr[i] = i;       

        }

        return arr;

    }

    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

        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]。

     

    解决方案

    在看解决方案一之前,我们先看一下匿名函数的自我执行:

    匿名函数自我执行的写法是,在函数体外面加一对圆括号,形成一个表达式,在圆括号后面再加一个圆括号,里面可传入参数。

    例如下代码:

    (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]);       

     

    解决方案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],

     

    案例详解

    <html >  

    <head>  

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  

    <title>闭包演示</title>  

    <script type="text/javascript">  

    function init() {  

      var pAry = document.getElementsByTagName("p");  

      for( var i=0; i<pAry.length; i++ ) {  

         pAry[i].onclick = function() {  

         alert(i);  

      }  

     }  

    }  

    </script>  

    </head>  

    <body onload="init();">  

    <p>产品一</p>  

    <p>产品二</p>  

    <p>产品三</p>  

    <p>产品四</p>  

    <p>产品五</p>  

    </body>  

    </html>

     

    解决方式有以下几种

    1、将变量 i 保存给在每个段落对象(p)上

    function init() {  

     var pAry = document.getElementsByTagName("p");  

     for( var i=0; i<pAry.length; i++ ) {  

       pAry[i].i = i;  

       pAry[i].onclick = function() {  

        alert(this.i);  

       }  

     }  

    }

     

    2、加一层闭包,i以函数参数形式传递给内层函数

    function init3() {  

     var pAry = document.getElementsByTagName("p");  

     for( var i=0; i<pAry.length; i++ ) {  

      (function(arg){    

        pAry[i].onclick = function() {    

         alert(arg);  

        };  

      })(i);//调用时参数  

     }  

    }  

     

    3、加一层闭包,i以局部变量形式传递给内存函数

    function init4() {  

     var pAry = document.getElementsByTagName("p");  

     for( var i=0; i<pAry.length; i++ ) {   

      (function () {  

       var temp = i;//调用时局部变量  

       pAry[i].onclick = function() {   

        alert(temp);   

       }  

      })();  

     }  

    }

     

     

     

  • 相关阅读:
    一篇文章教会你理解Scrapy网络爬虫框架的工作原理和数据采集过程
    Windows下安装Scrapy方法及常见安装问题总结——Scrapy安装教程
    Spring AOP里面的通知Advice类型
    Spring AOP面向切面编程核心概念
    ZeroC ICE的远程调用框架 Callback(一)-AMI异步方法调用框架
    ZeroC ICE的远程调用框架 class与interface
    ZeroC ICE的远程调用框架 AMD
    ZeroC ICE的远程调用框架
    ZeroC ICE中的对象模型和概念
    ZeroC ICE中的对象
  • 原文地址:https://www.cnblogs.com/ranyonsue/p/6439584.html
Copyright © 2020-2023  润新知