• 彻底弄懂js循环中的闭包问题


       第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然又想起了这个问题,在这个春气盎然的周末,我就坐下来研究下并把结果和大家分享下;

    先看代码:demo.html
    <!DOCTYPE HTML>
     <html>
      <head>
       <meta charset="gbk"/>
       <title>闭包循环问题</title>
       <style type="text/css">
         p {background:red;}
       </style>
     </head> 
     <body> 
      <p id="p0">段落0</p> 
      <p id="p1">段落1</p> 
      <p id="p2">段落2</p> 
      <p id="p3">段落3</p> 
      <p id="p4">段落4</p> 
    <script type="text/javascript"> 
     for( var i=0; i<5; i++ ) { 
       document.getElementById("p"+i).οnclick=function() { 
         alert(i); //访问了父函数的变量i, 闭包 
       };
     };
    </script> 
    </body> 
    </html>

      每次循环就为对应的编号段落上添加一个click事件,事件的回调函数是执行一个alert();如果你以前没这么用过的话,估计也会认为单击某个段落就会弹出这个段落相应的编号0,1,2,3,4。但实际上是都是弹出5;

    网上已经有很多讨论的博客了,他们给出了很多方法去实现弹出对应的编号。比较易于理解的方法如下:

    1,将变量i保存在对应的段落的某个属性上点击查看效果。

         var pAry = document.getElementsByTagName("p"); 
         for( var i=0; i< 5; i++ ) { 
          pAry[i].no = i; 
          pAry[i].onclick = function() { 
            alert(this.no); 
         } 
       };
    

    2,加一层闭包,i 以函数参数形式传递给内层函数点击查看效果。

     ~function test() {    
         var pAry = document.getElementsByTagName("p");    
         for( var i=0; i< pAry.length; i++ ) {    
          (function(arg){        
           pAry[i].onclick = function() {        
              alert(arg);    
           };    
          })(i);//调用时参数    
        }    
     }();  
    

    当然还有其他一些方法,但是都不太好理解。
        而我要探索的是,为什么demo.html中的返回值始终是5。网上的说法是“变量i是以指针或者变量地址方式保存在函数中”,因为只有按照这样理解,才能解释。可是仅仅凭借一个结论怎么才能服众了?

       谈到指针或者变量地址这个话题,在C语言中倒是家常便饭了,但是在js这么性感的语言中,除了对象的及其对象属性的引用之外很少用到。一个基本的数据类型居然和指针拉上关系了,这勾起了探索的欲望。

    3,试试下面的代码 点击查看效果 

    ~function test() {    
           var temp =10;  
           for( var i=0; i< 5; i++ ) { 
              document.getElementById("p"+i).οnclick=function() { 
                alert(temp); //访问了父函数的变量temp, 闭包 
              }
           };
           temp=20;
       }();
    

      它的执行结果是每个段落的弹出都是20,而不是10。说明当我们在单击的那个时候,temp的值已经是20。这是个似乎不需要我来说明,很显然的结果,因为在我们单击之前,temp已经被赋值为20了。

    4,再看看下面的代码  点击查看效果。
    我们在temp被改变值之前有程序去触发单击事件,弹出的是10;

     ~function test() {    
           var temp =10;  
           for( var i=0; i< 5; i++ ) { 
              document.getElementById("p"+i).οnclick=function() { 
                alert(temp); //访问了父函数的变量i, 闭包 
              }
            if(i===1){
               var evt = document.createEvent("MouseEvents");
               evt.initEvent("click",true,true);
               document.getElementById("p1").dispatchEvent(evt);
            }
           };
           temp=20;
       }();
    

      这说明我们在p1上绑定的单击事件回调函数本来是要返回10的,当我再次手动去单击p0段落时,弹出20的原因是因为temp的值改变了。也就说明,每次弹出时,访问到的是temp此刻的值,而不是绑定时候的值;这可以说明变量temp确实是以变量地址保存的。扩展开去就是:函数内部访问了与函数同级的变量,那么该变量是常驻内存的。访问该变量实质上是访问的是变量的地址;

       通过以上的结论,那么我们可以简单的描述闭包的本质了:在子作用域中保存了一份在父级作用域取得的变量,这些变量不会随父级作用域的销毁而销毁,因为他们已经常驻内存了!

    这句话也就说明了闭包的特性了:
    1:因为常驻内存所以会造成内存泄露
    2,只要其他作用域能取到子作用域的访问接口,那么其他作用域就有方法访问该子作用域父级作用域的变量了。

    看这样一典型的闭包的例子:
      function A(){
       var a=1;

       function B(){
        return a;
      }; 
      return B;
     };

     var C=A();//C取得A的子作用域B的访问接口
     console.log(C());//1 C能访问到B的父级作用域中的变量a 

    以上若有不足之处,欢迎指正,共同进步!

  • 相关阅读:
    统计nginx日志里访问次数最多的前十个IP
    while 格式化输出 运算符 字符编码
    Python 软件安装
    Python 基础
    Typora 基础的使用方法
    Django ORM (四) annotate,F,Q 查询
    Django 惰性机制
    Django ORM (三) 查询,删除,更新操作
    Django ORM (二) 增加操作
    Django ORM (一) 创建数据库和模型常用的字段类型参数及Field 重要参数介绍
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13317561.html
Copyright © 2020-2023  润新知