閉包(closure
)是Javascript
語言的一個難點,也是它的特色,很多高階應用都要依靠閉包實現。
閉包的特性
閉包有三個特性:
1 2 3 |
1.函式巢狀函式 2.函式內部可以引用外部的引數和變數 3.引數和變數不會被垃圾回收機制回收 |
閉包的定義及其優缺點
閉包
是指有權訪問另一個函式作用域中的變數的函式,建立閉包的最常見的方式就是在一個函式內建立另一個函式,通過另一個函式訪問這個函式的區域性變數
閉包的缺點就是常駐記憶體,會增大記憶體使用量,使用不當很容易造成記憶體洩露。
閉包是javascript
語言的一大特點,主要應用閉包場合主要是為了:設計私有的方法和變數。
一般函式執行完畢後,區域性活動物件就被銷燬,記憶體中僅僅儲存全域性作用域。但閉包的情況不同!
巢狀函式的閉包
1 2 3 4 5 6 7 8 9 10 |
function aaa() { var a = 1; return function(){ alert(a++) }; } var fun = aaa(); fun();// 1 執行後 a++,,然後a還在~ fun();// 2 fun = null;//a被回收!! |
閉包會使變數始終儲存在記憶體中,如果不當使用會增大記憶體消耗。
javascript
的垃圾回收原理
(1)、在javascript
中,如果一個物件不再被引用,那麼這個物件就會被GC
回收;
(2)、如果兩個物件互相引用,而不再被第3
者所引用,那麼這兩個互相引用的物件也會被回收。
使用閉包的好處
那麼使用閉包有什麼好處呢?使用閉包的好處是:
1 2 3 |
1.希望一個變數長期駐紮在記憶體中 2.避免全域性變數的汙染 3.私有成員的存在 |
一、全域性變數的累加
1 2 3 4 5 6 7 8 9 |
<script> var a = 1; function abc(){ a++; alert(a); } abc(); //2 abc(); //3 </script> |
二、區域性變數
1 2 3 4 5 6 7 8 9 10 |
<script> function abc(){ var a = 1; a++; alert(a); } abc(); //2 abc(); //2 </script> |
那麼怎麼才能做到變數a既是區域性變數又可以累加呢?
三、區域性變數的累加
1 2 3 4 5 6 7 8 9 10 11 12 |
<script> function outer(){ var x=10; return function(){ //函式巢狀函式 x++; alert(x); } } var y = outer(); //外部函式賦給變數y; y(); //y函式呼叫一次,結果為11,相當於outer()(); y(); //y函式呼叫第二次,結果為12,實現了累加 </script> |
函式宣告與函式表示式
在js中我們可以通過關鍵字function
來宣告一個函式:
1 2 3 4 5 6 |
<script> function abc(){ alert(123); } abc(); </script> |
我們也可以通過一個”()”來將這個宣告變成一個表示式:
1 2 3 4 5 |
<script> (function (){ alert(123); })(); //然後通過()直接呼叫前面的表示式即可,因此函式可以不必寫名字; </script> |
四、模組化程式碼,減少全域性變數的汙染
1 2 3 4 5 6 7 8 9 10 11 |
<script> var abc = (function(){ //abc為外部匿名函式的返回值 var a = 1; return function(){ a++; alert(a); } })(); abc(); //2 ;呼叫一次abc函式,其實是呼叫裡面內部函式的返回值 abc(); //3 </script> |
五、私有成員的存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<script> var aaa = (function(){ var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, //json結構 c:ccc } })(); aaa.b(); //2 aaa.c() //3 </script> |
六.使用匿名函式實現累加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//使用匿名函式實現區域性變數駐留記憶體中,從而實現累加 <script type="text/javascript"> function box(){ var age = 100; return function(){ //匿名函式 age++; return age; }; } var b = box(); alert(b()); alert(b()); //即alert(box()()); alert(b()); alert(b); // function () { // age++; // return age; // } b = null; //解除引用,等待垃圾回收 </script> |
過度使用閉包會導致效能的下降。函式裡放匿名函式,則產生了閉包
七、在迴圈中直接找到對應元素的索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title></title> <script> window.onload = function(){ var aLi = document.getElementsByTagName('li'); for (var i=0;i<aLi.length;i++){ aLi[i].onclick = function(){ //當點選時for迴圈已經結束 alert(i); }; } } </script> </head> <body> <ul> <li>123</li> <li>456</li> <li>789</li> <li>010</li> </ul> </body> </html> |
八、使用閉包改寫上面程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title></title> <script> window.onload = function(){ var aLi = document.getElementsByTagName('li'); for (var i=0;i<aLi.length;i++){ (function(i){ aLi[i].onclick = function(){ alert(i); }; })(i); } }; </script> </head> <body> <ul> <li>123</li> <li>456</li> <li>789</li> </ul> </body> </html> |
九.記憶體洩露問題
由於IE
的js
物件和DOM
物件使用不同的垃圾收集方法,因此閉包在IE
中會導致記憶體洩露問題,也就是無法銷燬駐留在記憶體中的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function closure(){ var oDiv = document.getElementById('oDiv');//oDiv用完之後一直駐留在記憶體中 oDiv.onclick = function () { alert('oDiv.innerHTML');//這裡用oDiv導致記憶體洩露 }; } closure(); //最後應將oDiv解除引用來避免記憶體洩露 function closure(){ var oDiv = document.getElementById('oDiv'); var test = oDiv.innerHTML; oDiv.onclick = function () { alert(test); }; oDiv = null; } |