• 从javascript的循环问题来看待闭包本质


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

    先看代码:demo.html

     1 <!DOCTYPE HTML>
     2 <html>
     3 <head>
     4 <meta charset="utf-8"/>
     5 <title>闭包循环问题</title>
     6 <style type="text/css">
     7     p {background:red;}
     8 </style>
     9 </head> 
    10 <body> 
    11 <p id="p0">段落0</p> 
    12 <p id="p1">段落1</p> 
    13 <p id="p2">段落2</p> 
    14 <p id="p3">段落3</p> 
    15 <p id="p4">段落4</p> 
    16 <script type="text/javascript" src="jquery-1.7.js"></script>
    17 <script type="text/javascript"> 
    18 ~function test() {   
    19     for( var i=0; i<5; i++ ) { 
    20          $("#p"+i).bind("click", function() {    
    21              alert(i);    
    22           });
    23   };
    24 }()
    25 </script> 
    26 </body> 
    27 </html>

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

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

    1,将变量i保存在对应的段落的某个属性上:

    1 ~function test() {   
    2     for( var i=0; i<5; i++ ) { 
    3          $("#p"+i).bind("click", function() {
    4              alert($(this).attr("index"));    
    5           }).attr("index",i);
    6   };
    7 }();

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

     1 ~function test() {   
     2     for( var i=0; i<5; i++ ) { 
     3          (function(param){
     4              $("#p"+i).bind("click", function() {
     5                 alert(param);  
     6           });
     7          })(i);
     8         
     9   };
    10 }()

    当然还有其他一些方法,但是都不太好理解。

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

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

    3,试试下面的代码

    1 ~function test() {   
    2     var temp =10;
    3     for( var i=0; i<5; i++ ) { 
    4         $("#p"+i).bind("click",function() {
    5                 alert(temp);    
    6          });
    7     };
    8     temp=20;
    9 }();
    它的执行结果是每个段落的弹出都是20,而不是10。说明当我们在单击的那个时候,temp的值已经是20。这是个似乎不需要我来说明,很显然的结果,因为在我们单击之前,temp已经被赋值为20了。
    4,再看看下面的代码,我们在temp被改变值之前有程序去触发单击事件,弹出的是10;
     1 ~function test() {   
     2     var temp =10;
     3     for( var i=0; i<5; i++ ) { 
     4         $("#p"+i).bind("click",function() {
     5                 alert(temp);    
     6          });
     7          if(i===1){
     8           $("#p0").trigger("click");
     9          };
    10     };
    11     temp=20;
    12 }();

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

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

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

    看这样一典型的闭包的例子:

     1 function A(){
     2    var a=1;
     3     
     4     function B(){
     5         return a++;
     6    };
     7    
     8     return  B;
     9 };
    10 
    11 
    12 var  C=A();//C取得A的子作用域B的访问接口
    13 console.log(C());//2    C能访问到B的父级作用域中的变量a   

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

    开心一刻:

  • 相关阅读:
    AOP AspectJ 字节码 语法 MD
    判断小米华为等系统 MD
    vuejs2.0实现分页组件,使用$emit进行事件监听数据传递
    vuejs2.0实现一个简单的分页
    vuejs2.0使用Sortable.js实现的拖拽功能
    JavaScript之Number、String、Array常用属性与方法手册
    CSS3效果:5种预载动画效果
    vuejs 1.x
    window.requestAnimationFrame与Tween.js配合使用实现动画缓动效果
    如何用JavaScript判断dom是否有存在某class的值?
  • 原文地址:https://www.cnblogs.com/wenber/p/3632428.html
Copyright © 2020-2023  润新知