閉包:指有權訪問另外一個函數做用域中的變量的函數。
閉包的本質是將函數內部和函數外部鏈接起來的一座橋樑。javascript
例1:java
function outer(){ var a=1; function inner(){ a++; alert(a); } return inner; } var f1=outer();//建立了一個閉包,f1能訪問outer函數中的變量 f1();//彈出2.
這段代碼的特色在於:
1.函數inner嵌套在函數outer內部
2.函數outer返回函數inner,並將值賦給了f1
例2:算法
// 實現累加:方式1 var a = 0; var add = function(){ a++; console.log(a) } add(); add(); //方式2 :閉包 var add = (function(){ var a = 0; return function(){ a++; console.log(a); } })(); console.log(a); //undefined add(); add(); //方式2的優勢:減小全局變量,將變量私有化
閉包只能取得包含函數中任何變量的最後一個值。
例:chrome
function f1() { var res = new Array(); for(var i=0;i<10;i++){ res[i] = function() { alert(i); }; } return res; } var f2 = f1(); var f2 = f1(); f2[0]();//alert 10 //並不會返回一次彈出0-9的函數數組,而是彈出10個10的函數數組,由於res中每一個函數的做用域中都保存着f1()的活動對象,引用的是同一個變量i,當f1()返回後i的值爲10
解決方法:數組
function f1() { var res = new Array(); for(var i=0;i<10;i++){ res[i] = (function(num) { return function (){ alert(num); } })(i);//函數參數按值傳遞 } return res; } var f2 = f1(); var f2 = f1(); f2[0]();//alert 0
垃圾回收機制閉包
說到內存管理,天然離不開JS中的垃圾回收機制,有兩種策略來實現垃圾回收:標記清除 和 引用計數;函數
標記清除:垃圾收集器在運行的時候會給存儲在內存中的全部變量都加上標記,而後,它會去掉環境中的變量的標記和被環境中的變量引用的變量的標記,此後,若是變量再被標記則表示此變量準備被刪除。 2008年爲止,IE,Firefox,opera,chrome,Safari的javascript都用使用了該方式;設計
引用計數:跟蹤記錄每一個值被引用的次數,當聲明一個變量並將一個引用類型的值賦給該變量時,這個值的引用次數就是1,若是這個值再被賦值給另外一個變量,則引用次數加1。相反,若是一個變量脫離了該值的引用,則該值引用次數減1,當次數爲0時,就會等待垃圾收集器的回收。指針
這個方式存在一個比較大的問題就是循環引用,就是說A對象包含一個指向B的指針,對象B也包含一個指向A的引用。 這就可能形成大量內存得不到回收(內存泄露),由於它們的引用次數永遠不多是 0 。早期的IE版本里(ie4-ie6)採用是計數的垃圾回收機制,閉包致使內存泄露的一個緣由就是這個算法的一個缺陷。code
咱們知道,IE中有一部分對象並非原生額javascript對象,例如,BOM和DOM中的對象就是以COM對象的形式實現的,而COM對象的垃圾回收機制採用的就是引用計數。所以,雖然IE的javascript引擎採用的是標記清除策略,可是訪問COM對象依然是基於引用計數的,所以只要在IE中設計COM對象就會存在循環引用的問題!
例子:
window.onload = function(){ var ele = document.getElementById("id"); ele.onclick = function(){ alert(ele.id); } }
這段代碼爲何會形成內存泄露?
ele.onclick = function(){ alert(ele.id); }
執行這段代碼的時候,將匿名函數對象賦值給ele的onclick屬性;而後匿名函數內部又引用了ele對象,存在循環引用,因此不能被回收。
解決方法:
window.onload = function(){ var ele = document.getElementById("id"); var id = ele.id; //解除循環引用 ele.onclick = function(){ alert(id); } ele = null; // 將閉包引用的外部函數中活動對象清除 }
優勢:
當須要一個變量常駐內存時,閉包能夠實現一個變量常駐內存 (若是多了就佔用內存了)
避免全局變量的污染
私有化變量
缺點:
由於閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存
引發內存泄露