分分钟搞懂JS-闭包函数

 1 //抛开概念!直接上代码,看实例
 2 
 3 var i=100;
 4 function add1(){
 5    alert(i); 
 6    i++;
 7 }
 8 function add2(){
 9    var j=100;
10    alert(j); 
11    j++; 
12 }
13 
14 //测试;
15 
16 //    add1(); //100
17 //    add1(); //101
18 //    add1(); //102
19 //    
20 //    add2(); //100
21 //    add2(); //100
22 //    add2(); //100
23 
24 /*为什么呢 原因很简单
25 i 是全局变量,只有页面关闭的时候,才会被GC回收;
26 j 是局部变量,函数执行完毕后就 被GC 回收;
27 
28 问:有没有办法将j 变成"全局变量"呢?
29 A同学答:有,将j写在外面(尼玛,傻逼啊)
30 B同学答,用return(有点接近了) 然后用全句变量来接收;
31 C同学答:闭包(完美)    
32 */
33 function add3(){
34   var j=100;
35   function fun(){
36    alert(j);
37    j++;
38   }
39    return fun; //注意这里;
40 }
41 
42 /*
43 测试:
44 
45 var obj=add3(); //执行add3()函数,同时返回了fun的引用(也就是fun的函数体)
46 
47 //直接alert(obj) 你会看到他的函数体- function fun(){....} 再加上一个括号(),就可以执行了
48 
49    obj(); //100
50 
51    obj(); //101
52 
53    obj();//102
54 
55  */

 

当函数a的内部函数b 被函数a 外的一个变量引用的时候,就创建了一个闭包。

简而言之,闭包的作用就是在a执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。

那么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引 用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC回收

最后,如果你能看懂这段代码你就理解了闭包!

 1    var name="window";
 2    var object={
 3      name:"my object",
 4      getName:function (){
 5          return function (){
 6              return this.name; 
 7          }
 8      }
 9       
10   }
11  alert(object.getName()()); //window

闭包的多种写法:

写法一:

  function f1(){
     var index=1;
     function f2(){
        alert(index);
        index++; 
     }
     return f2;
  }
  //调用
  var test=f1();
  test(); //1
  test(); //2
  test(); //3

写法二:

  function outer(){
      var index=1;
     return function(){ //使用匿名函数
         alert(index);
         index++;
     }
  }
 var bibao=outer();
  bibao();
  bibao();
  bibao();

写法三: 

var outer=(function (){
      var index=1;
      return function (){
         alert(index);
         index++;  
      }
    
})();

outer();
outer();
outer();

写法四:

var outer=null;
(function (){
    var index=1;
    function inner(){
      alert(index++);    
    };
    outer=inner;
})();
outer();
outer();
outer();

总之就是函数之间的嵌套,变量之间的互相引用;

javascript的垃圾回收原理

(1)、在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收; 
(2)、如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

 总结:

 

 

实例:

html代码:

          <ul>
                    <li>123</li>
                    <li>456</li>
                    <li>789</li>
                    <li>010</li>
            </ul>

 js代码:(错误方式) 

window.onload = function(){
            var lis = document.getElementsByTagName('li');
            var len=lis.length;
            for (var i=0;i<len;i++){
                    lis[i].onclick = function(){        //当点击时for循环已经结束
                    alert(i);
                    };
            }
    }

js代码:(错误方式)

window.onload=function (){
     var index=0;
     var lis=document.getElementsByTagName("li");
     var len=lis.length;
     for(var i=0;i<len;i++){
         lis[i].onclick=function (){
            alert(index);  //这样做,避免了i直接等于3,但是,却没有绑定到指定的对象上滴呀
            index++;
            if(index==len){
                index=0;
            }
         }
     }
}

js代码:(改进方式一)面向对象+this关键字的使用

window.onload=function (){
  var lis=document.getElementsByTagName("li");
  var len=lis.length;
  for(var i=0;i<len;i++){
      lis[i].index=i; //给每个变量动态的绑定一个属性,并赋值
      lis[i].onclick=function (){
          var temp=this.index; //然后使用this关键字;
          alert(temp);  
      }
  }
}

js代码改进二:让i独立出来,index作为他的一个“副本”! i的变化不再影响到我们index

function showInfo(obj,index){
   obj.onclick=function (){
     alert(index);   
   }
}

window.onload=function (){
  var lis=document.getElementsByTagName("li");
  var len=lis.length;
  for(var i=0;i<len;i++){
     showInfo(lis[i],i);//i的改变和 index无关滴呀  
  }
}

原理解析一:

   var index=1;
   
   function change1(){
     index=index+1;
   }
   
   function change2(){
    index=index+2;
   }
   change1();
   change2();
   alert(index); //4

原理解析二:

   function change1(val){
     var temp=val;
     temp=temp+1;
   }
   
   function change2(val){
     var temp=val;
     temp=temp+2;
   }
   
   change1(index);
   change2(index);
   alert(index);
   //这样这不会改变;

最总版本:(关键-传递副本)

 var index=1;
  (function (val){
     val=100;  
  })(index);
   
  (function (val){
     val=10000;  
  })(index);
   
   alert(index);

js代码:(闭包实现)模块化代码,减少全局变量的污染-最好的方式;

我们需要在每次循环时为变量 i 值创建一个拷贝,重点在:模块话代码,形成一个独立的区域;

window.onload=function (){
     var lis=document.getElementsByTagName("li");
     var len=lis.length;
     for(var i=0;i<len;i++){
         (function (e){
              lis[e].onclick=function (){
                 alert(e);  
              }
         })(i); //形成一个独立的区域互补干扰
     }
}

继续扩展,这段代码是不是闭包?还是仅仅是一个你们匿名函数的自执行呢? 

         (function (e){
              lis[e].onclick=function (){
                 alert(e);  
              }
         })(i)

 

 //闭包概念:闭包是指某种程序语言中的代码块允许一级函数存在并且
  //在一级函数中所定义的自由变量不能被释放,直到一级函数释放前,
  //一级函数外也能应用这些未释放的自由变量;
 var shit=function (y){
    var x=y;
    return function (){
      alert(x++); //这里调用了一级函数的局部变量x
      //上下连个交换着尝试
      alert(y--); //这里调用参数变量,是自变量;    
    }}(5);
    
shit();  // 5 5
shit();  // 6 4
shit();  // 7 3

 

关于内存泄露的问题;

function closure(){
   var div=document.getElementById("div1");//div用完之后一直驻留在内存中
   div.onclick=function (){
      alert(div.innerHTML);  //这里导致了内存泄露得呀
      //长期扎住在内存中得,没有得到是释放;   
   }
}

//优化:
function closure2(){
  var div=document.getElementById("div1");
  var text=div.innerHTML;
   div.onclick=function (){
      alert(text);  //这里导致了内存泄露得呀
      //长期扎住在内存中得,没有得到是释放;   
   }
   div=null;
}

再推荐一篇关于闭包的好文章

http://segmentfault.com/a/1190000000652891

应用

看我另外一篇文章:

js 动画实现原理,就是闭包的一个典型应用的呀;

 

 

 

 

 

 

posted @ 2015-09-12 14:48  咕-咚  阅读(605)  评论(0编辑  收藏  举报