完全弄懂js循环中的闭包问题

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

先看代码: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).onclick=function() { 
     alert(i); //访问了父函数的变量i, 闭包 
   };
 };
</script> 
</body> 
</html>

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

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

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

     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这么性感的语言中,除了对象的及其对象属性的引用以外不多用到。一个基本的数据类型竟然和指针拉上关系了,这勾起了探索的欲望。this

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

~function test() {    
       var temp =10;  
       for( var i=0; i< 5; i++ ) { 
          document.getElementById("p"+i).onclick=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).onclick=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确实是以变量地址保存的。扩展开去就是:函数内部访问了与函数同级的变量,那么该变量是常驻内存的。访问该变量实质上是访问的是变量的地址;htm

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

这句话也就说明了闭包的特性了:
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 以上如有不足之处,欢迎指正,共同进步!

相关文章
相关标签/搜索