一、什么是閉包?
看概念總是迷迷糊糊,好像懂了,卻又說不清。在此引用別的博主的話:
通俗地講就是別人家有某個東西,你想拿到但是因為權限不夠(不打死你才怪),但是你可以跟家里的孩子套近乎,通過他拿到!
這個家就是局部作用域,外部無法訪問內部變量,孩子是返回對象,對家里的東西有訪問權限,借助返回對象間接訪問內部變量!
總結下有三個特點:
-
函數嵌套函數
-
內部的函數可以引用外部函數的參數或者變量
-
參數和變量不會被垃圾回收機制回收,因為內部函數還在引用
function aaa(){
var a = 5;
function bbb(){
console.log(a);
}
return bbb;
}
var c = aaa(); //此時c是aaa內部return的bbb函數體,外部函數aaa已運行完畢,但是變量仍被內部函數引用,故不會釋放。
c(); //打印結果是5
二、閉包的好處
-
變量可以長期駐扎在內存之中
-
避免全局變量的污染,有私有成員
下面看一下例子:
1、普通函數調用:aaa執行完畢,就回收a變量,再次執行,重新賦值計算。
function aaa(){
var a = 1;
a++;
alert(a);
}
aaa(); //2
aaa(); //2
2、閉包方式調用:aaa執行后,由於a變量還在被內部函數引用,故不會被回收,再次計算,在上一次的結果上進行累加。
function aaa(){
var a = 1;
return function(){
a++;
alert(a);
}
}
var c = aaa();
c(); //2
c(); //3
3、上面還可以改寫成:
var aaa = (function(){
var a = 1;
return function(){
a++;
alert(a);
}
})() //函數表達式自執行,結果是內部函數體
aaa(); //2
aaa(); //3
三、閉包的用途
-
模塊化代碼
var aaa = (function(){ //aaa是一個模塊,私有變量是a,私有函數是bbb和ccc var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, c:ccc } })() //函數表達式自執行,結果是return后面的對象 aaa.b(); //2 aaa.c(); //3
- 在循環中找到索引
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = function(){
alert(i); //順序點擊三個li,分別彈出3 3 3。因為點擊的時候for循環已經執行完畢
}
}
}
</script>
</body>
可使用閉包改為:
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<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); //0 1 2
}
})(i) //將i作為參數傳遞給內部函數,i 在for循環執行完畢不會被釋放
}
}
</script>
</body>
或者另一種寫法:
<body>
<ul>
<li>111111</li>
<li>111111</li>
<li>111111</li>
</ul>
<script>
window.onload = function(){
var aLi = document.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = (function(i){
return function(){
alert(i);
}
})(i) //循環的時候,這個函數表達式自執行,i也是駐扎在內存當中。
}
}
</script>
</body>
四、閉包需要注意的問題
-
在IE下會引發內存泄露
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onclick = function(){
alert(oDiv.id);
}
}
</script>
</body>
當一個變量為一個dom節點獲取或者宿主對象(此例子中為oDiv),它的一個屬性(此例中為onclick)引用一個內部函數,而內部函數又在引用外部對象(此例中外部對象是oDiv)。會造成互相引用,引起內存泄露。
- 如何解決:
方法一:
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onclick = function(){
alert(oDiv.id);
}
//卸載文檔時候執行
window.onunload=function(){
oDiv.onclick = null;
}
}
</script>
</body>
方法二:
<body>
<div id="div1">my div</div>
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
//賦值給一個變量
var oId = oDiv.id;
oDiv.onclick = function(){
alert(oId);
}
//還要讓對象對空
oDiv = null;
}
</script>
</body>