其實關于閉包各個論壇社區里都有很多的文章來講它,畢竟閉包是JavaScript中一個特色,也正因為這個雨中不同的特色也讓閉包理解起來有一些吃力。筆者在這里不僅僅是想介紹閉包,也向列舉一些筆者所見過的一些閉包,如果有讀者還有一些比較經典的閉包例子,希望可以在評論區里留一下,謝謝。

  說了半天,究竟什么是閉包呢?

  • 閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。

  • 閉包就是就是函數的“堆棧”在函數返回后并不釋放,我們也可以理解為這些函數堆棧并不在棧上分配而是在堆上分配。

  • 當在一個函數內定義另外一個函數就會產生閉包。

  為了便于理解,我們可以簡單的將閉包理解為:

  閉包:是指有權訪問另外一個函數作用域中的變量的函數。

  JavaScript中的作用域

  JavaScript中是沒有塊級作用域的。不過關于塊級作用域我們在這里不做深入探究。

  變量的作用域無非就是兩種:全局變量和局部變量。

  Javascript語言的特殊之處,就在于函數內部可以直接讀取全局變量。

JavaScript代碼
  1. var n=999;  
  2. function f1(){  
  3.   alert(n);  
  4. }  
  5. f1(); // 999  

  如上函數,f1可調用全局變量n

  另一方面,在函數外部自然無法讀取函數內的局部變量。

JavaScript代碼
  1. function f1(){  
  2.   var n=999;  
  3. }  
  4. alert(n); // error  

  這里有一個地方需要注意,函數內部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量。

JavaScript代碼
  1. function f1(){  
  2.   n=999;  
  3. }  
  4. f1();  
  5. alert(n); // 999  

  閉包

  1. 理解閉包

  我們已經理解了什么是作用域,什么是塊級作用域,那又該如何去訪問函數內部的變量呢?

  出于種種原因,我們有時候需要得到函數內的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。

JavaScript代碼
  1. function f1(){  
  2.    var n=999;  
  3.    function f2(){  
  4.      alert(n);  
  5.      }   
  6.       return f2;  
  7. }  
  8. var result=f1();  
  9. result();// 彈出999  

  上面函數中的f2函數就是閉包,就是通過建立函數來訪問函數內部的局部變量。

  2. 閉包的用途

  閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。

JavaScript代碼
  1. function f1(){  
  2.   var n=999;  
  3.   nAdd=function(){n+=1}  
  4.   function f2(){  
  5.     alert(n);  
  6.   }  
  7.   return f2;  
  8. }  
  9. var result=f1();  
  10. result(); // 999  
  11. nAdd();  
  12. result(); // 1000  

  在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。

  為什么會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。

  這段代碼中另一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。

  3. 閉包的注意點

  1)由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。

  2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。

  4. 經典閉包小案例

  如果你能理解下面全部的案例,那你的閉包就算是真正掌握了。

JavaScript代碼
  1. var name = "The Window";  
  2. var object = {  
  3.   name : "My Object",  
  4.   getNameFunc : function(){  
  5.     return function(){  
  6.       return this.name;  
  7.     };  
  8.   }  
  9. };  
  10. alert(object.getNameFunc()());//The Window  
JavaScript代碼
  1. var name = "The Window";  
  2. var object = {  
  3.   name : "My Object",  
  4.   getNameFunc : function(){  
  5.     var that = this;  
  6.     return function(){  
  7.       return that.name;  
  8.     };  
  9.   }  
  10. };  
  11. alert(object.getNameFunc()());//My Object  
JavaScript代碼
  1. function fun(n,o) {  
  2.   console.log(o)  
  3.   return {  
  4.     fun:function(m){  
  5.       return fun(m,n);  
  6.     }  
  7.   };  
  8. }  
  9. var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?  
  10. var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?  
  11. var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?  

  //問:三行a,b,c的輸出分別是什么?

  這是一道非常典型的JS閉包問題。其中嵌套了三層fun函數,搞清楚每層fun的函數是那個fun函數尤為重要。

  //答案:

  //a: undefined,0,0,0

  //b: undefined,0,1,2

  //c: undefined,0,1,1

  都答對了么?如果都答對了恭喜你在js閉包問題當中幾乎沒什么可以難住你了。

  Happy hacking!

除非特別注明,雞啄米文章均為原創
轉載請標明本文地址:http://www.9385095.live/software/633.html
2016年9月19日
作者:雞啄米 分類:軟件開發 瀏覽: 評論:0