1.什麼是閉包?
a.通俗點講就是函數嵌套函數
b.內部函數可以調用外部函數中的變量(局部變量和全局變量)和函數
如下代碼:
function cat(miao){
var wang = 'wangwang!!!';
function dog(){
alert(miao + wang);
}
}
c.函數中的變量和參數在外部函數調用完畢的時候不會被垃圾回收機制從內存中釋放(也就是說會長期駐紮在內存中)
註釋:通常一般的函數在調用完畢後垃圾回收機制就會將變量分配的內存進行收回
如下案例:當執行完var mimi = cat();
的時候實際上cat()
函數已經執行完畢,如果垃圾回收機制執行的話,那mimi();
執行完畢的時候就不會彈出'wangwang'
,但是實際上這個結果執行出來了,說明在外部函數調用完畢的時候變量和參數沒有被垃圾回收機制收回內存,而是駐留在內存中沒有被釋放。
function cat(){
var wang = 'wangwang!!!';
function dog(){
alert(wang);
}
return dog ;
}
var mimi = cat();
mimi();
2.閉包有什麼應用?
a.避免全局變量的污染
如下代碼:
var age = 99 ;
function person(){
age++ ;
alert(age);
}
person();//100
person();//101
alert(age);//101
上面的代碼age變量就可以被外界調用就不符合條件,全局變量被污染了。同時程序爲了提高性能,最好不要出現全局變量。修改代碼使age變成局部變量,如下代碼:
function person(){
var age = 99 ;
age++;
alert(age);
}
person();//100
person();//100
上面的代碼雖然實現了變量的局部化,同時不能被外界調用age變量了,但是發現功能沒有實現了,每次函數執行完畢一次age就被垃圾回收機制收回內存了,重新執行。所以不能滿足程序中age年齡遞增的需求。其實閉包可以做到這一點,如下代碼:
function person(){
var age = 99 ;
return function(){
age++;
alert(age);
}
}
var bill = person();
bill();//100
bill();//101
alert(age);//age is not defined
上面這段代碼用閉包實現了功能。但是發現這種函數寫法是不是很彆扭呢?我是這種感覺,其實我們還可以繼續用JavaScript中的模塊化代碼將上述代碼中的函數改寫成表達式自執行的方式,就簡便多了。改寫代碼如下:
var person = (function(){
var age = 99 ;
return function(){
age++;
alert(age);
}
})();
person();//100
person();//101
上述代碼中出現一個新的寫法就是函數自執行,什麼是函數自執行呢?寫法就是這樣的(function(){})();
其中函數中的內容會自己執行。
b.私有成員的存在
其實上面的模塊化代碼還有一個好處就是可以對私有變量和私有函數進行封裝在一個密閉的空間中,外部是無法訪問的。如下代碼:
var module = (function(){
var number = 66 ;
function increase(){
alert(++number);
}
function desc(){
alert(--number);
}
return {
cat:increase,
dog:desc
};
})();
module.cat();//67
module.dog();//66
其實大名鼎鼎的jQuery框架就採用了上面的模塊化封裝思想。
3.閉包有什麼好處,使用的時候要注意什麼?
a.索引值的問題
當點擊li的時候顯示li在ul中的索引,如下代碼:
var objLi = document.getElementsByTagName('li');
for(var i = 0 ; i < objLi.length ; i++){
objLi[i].onclick = function(){
alert(i);
}
}
發現如果代碼這樣寫的話,當點擊的時候顯示的都是ul中所有li的總和。根本不能顯示li的索引。這樣就可以用閉包進行改進,因爲閉包可以封裝局部變量不收外界影響,如下代碼:
var objLi = document.getElementsByTagName('li');
for(var i = 0 ; i < objLi.length ; i++){
(function(i){
objLi[i].onclick = function(){
alert(i);
}
})(i);
}
當然還可以像下面這樣進行封裝代碼:
var objLi = document.getElementsByTagName('li');
for(var i = 0 ; i < objLi.length ; i++){
objLi[i].onclick = (function(i){
return function(){
alert(i);
}
})(i);
}
b.IE下引發內存泄露問題
如下代碼:
var objDiv = document.getElementById('divOne');
objDiv.onclick = function(){
alert(objDiv.id);
}
就是像上面這段代碼會引發內存泄露,爲什麼呢?因爲在點擊事件中引用外部對象,外部對象在函數執行完畢的時候內存無法釋放,怎樣解決這個IE下的問題呢?
var objDiv = document.getElementById('divOne');
var id = objDiv.id;
objDiv.onclick = function(){
alert(id);
}
objDiv = null ;
在上面的代碼中就是講變量局部化,然後用完對象進行手動設置爲null進行內存釋放。這樣問題就可以解決了。