我們先看一個正常的for循環,普通函數裡面有一個for循環,for循環結束後最終傳回結果數組
function box(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = i;
}
return arr;
}
alert(box())
//正常情況不需要閉包,就可以達到預期效果,輸出結果為一個數組0,1,2,3,4
有時我們需要在for循環裡面添加一個匿名函數來實作更多功能,看下面代碼
//循環裡面包含閉包函數
arr[i] = function(){
return i; //由于這個閉包的關系,他是循環完畢之後才傳回,最終結果是4++是5
} //這個匿名函數裡面根本沒有i這個變量,是以匿名函數會從父級函數中去找i,
} //當找到這個i的時候,for循環已經循環完畢了,是以最終會傳回5
//alert(box()); //執行5次匿名函數本身
//alert(box()[1]); //執行第2個匿名函數本身
//alert(box().length); //最終傳回的是一個數組,數組的長度為5
alert(box()[0]()); //數組中的第一個數傳回的是5,這是為什麼?
上面這段代碼就形成了一個閉包:
閉包是指有權通路另一個函數作用域中的變量的函數,建立閉包的常見的方式,就是在一個函數内部建立另一個函數,通過另一個函數通路這個函數的局部變量。
在for循環裡面的匿名函數執行 return i 語句的時候,由于匿名函數裡面沒有i這個變量,是以這個i他要從父級函數中尋找i,而父級函數中的i在for循環中,當找到這個i的時候,是for循環完畢的i,也就是5,是以這個box得到的是一個數組[5,5,5,5,5]。
解決方案
在看解決方案一之前,我們先看一下匿名函數的自我執行:
匿名函數自我執行的寫法是,在函數體外面加一對圓括号,形成一個表達式,在圓括号後面再加一個圓括号,裡面可傳入參數。
例如下代碼:
(function(){
alert('lee'); //匿名函數自我執行(匿名函數)()
})();
解決方案1:
arr[i] = (function(num){ //自我執行,并傳參(将匿名函數形成一個表達式)(傳遞一個參數)
return num; //這裡的num寫什麼都可以
})(i); //這時候這個括号裡面的i和上面arr[i]的值是一樣的都是取自for循環裡面的i
}
//alert(box());
//alert(box()[1]);
//alert(box().length);
alert(box()[0]);
解決方案2
這種方案的原理就是在匿名函數1裡面再寫入一個匿名函數2,這個匿名函數2需要的num值會在他的父級函數匿名函數1裡面去尋找,而匿名函數1裡面的num值就是傳入的這個參數i,和上面例子中的i是一樣的,
arr[i] = (function(num){
//num在這裡 //原理和上面一種方法一樣的,是以可以實作閉包
return function(){ //在這個閉包裡面再寫一個匿名函數
return num;
};
})(i)
//alert(box().length);
var b = box();
alert(b[0]());
alert(box()[0]());
box()最終傳回結果[0,1,2,3,4],
案例詳解
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>閉包示範</title>
<script type="text/javascript">
function init() {
var pAry = document.getElementsByTagName("p");
for( var i=0; i<pAry.length; i++ ) {
pAry[i].onclick = function() {
alert(i);
}
}
}
</script>
</head>
<body onload="init();">
<p>産品一</p>
<p>産品二</p>
<p>産品三</p>
<p>産品四</p>
<p>産品五</p>
</body>
</html>
解決方式有以下幾種
1、将變量 i 儲存給在每個段落對象(p)上
var pAry = document.getElementsByTagName("p");
for( var i=0; i<pAry.length; i++ ) {
pAry[i].i = i;
pAry[i].onclick = function() {
alert(this.i);
}
2、加一層閉包,i以函數參數形式傳遞給内層函數
function init3() {
(function(arg){
pAry[i].onclick = function() {
alert(arg);
};
})(i);//調用時參數
3、加一層閉包,i以局部變量形式傳遞給記憶體函數
function init4() {
for( var i=0; i<pAry.length; i++ ) {
(function () {
var temp = i;//調用時局部變量
pAry[i].onclick = function() {
alert(temp);
})();