【轉載】解決閉包的9種方法
1.正確的說,應該是指一個閉包域,每當聲明瞭一個函式,它就產生了一個閉包域(可以解釋為每個函式都有自己的函式棧),每個閉包域(Function 物件)都有一個 function scope(不是屬性),function scope內預設有個名為Global的全域性引用(有了這個引用,就可以直接呼叫 Global 的屬性或方法)
2.凡是在閉包域內宣告的變數或方法,外部無法直接訪問
3.閉包域可以訪問外部的變數或方法
(上圖為 chrome 下 debug 環境)
當在一個閉包域內包含另一個閉包域時(簡單的說就是在一個函式內有另一個函式,當然這個內部函式的生命週期是依附於外部函式的), 此時,若子閉包域(內部的閉包域,內部函式)使用了父閉包域(外部閉包域,外部函式)的私有變數(在父閉包域中宣告的變數,父閉包域的外部空間無法直接訪問,但子閉包域可以訪問),子閉包域即當前的子函式的 function scope 會產生一個 closure 物件屬性,這個物件屬性內包含的是子閉包域對父閉包域的所有引用(只要子閉包域(內部函式)還存活,其父閉包域(外部函式)就依舊存活),倘若在父閉包域存活期間對其私有變數內容進行修改,則對這些父閉包域的私有變數進行引用的子閉包域中 function scope 的 closure 物件屬性的內容也會發生變化,因為這只是引用.
舉例:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title></title>
</head>
<body>
<scripttype="text/javascript"charset="utf-8">
//函式 a 有一個私有變數 p 和一個內部函式 innerA
function a(){//外部閉包域 ,一個名為 a 的 Function 物件
var p =0;//私有變數 p
var innerA =function(){//內部閉包域 ,一個名為 innerA 的 Function 物件
console.log(p);//對外部閉包域的私有變數進行了引用,故 innerA 物件的 function scope 會產生一個名為 closure 的物件屬性,closure 物件內含有一個名為 p 的引用
}
innerA();//輸出0
p++;
innerA();//輸出1
}
a();
</script>
</body>
</html>
結果如下:
第一次呼叫innerA
第二次呼叫 innerA
控制檯輸出
回到主題 面試經典問題
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title></title>
<scripttype="text/javascript">
//面試經典問題:
function onMyLoad(){
/*
丟擲問題:
此題的目的是想每次點選對應目標時彈出對應的數字下標 0~4,但實際是無論點選哪個目標都會彈出數字5
問題所在:
arr 中的每一項的 onclick 均為一個函式例項(Function 物件),這個函式例項也產生了一個閉包域,
這個閉包域引用了外部閉包域的變數,其 function scope 的 closure 物件有個名為 i 的引用,
外部閉包域的私有變數內容發生變化,內部閉包域得到的值自然會發生改變
*/
var arr = document.getElementsByTagName("p");
for(var i =0; i < arr.length;i++){
arr[i].onclick =function(){
alert(i);
}
}
}
</script>
</head>
<bodyonload="onMyLoad()">
<p>產品一</p>
<p>產品二</p>
<p>產品三</p>
<p>產品四</p>
<p>產品五</p>
</body>
</html>
解決辦法:
解決辦法一
/*
解決思路:
增加若干個對應的閉包域空間(這裡採用的是匿名函式),專門用來儲存原先需要引用的內容(下標),不過只限於基本型別(基本型別值傳遞,物件型別引用傳遞)
*/
for(var i =0;i<arr.length;i++){
//宣告一個匿名函式,若傳進來的是基本型別則為值傳遞,故不會對實參產生影響,
//該函式物件有一個本地私有變數arg(形參) ,該函式的 function scope 的 closure 物件屬性有兩個引用,一個是 arr,一個是 i
//儘管引用 i 的值隨外部改變 ,但本地私有變數(形參) arg 不會受影響,其值在一開始被呼叫的時候就決定了.
(function(arg){
arr[i].onclick =function(){//onclick函式例項的 function scope 的 closure 物件屬性有一個引用 arg,
alert(arg);//只要 外部空間的 arg 不變,這裡的引用值當然不會改變
}
})(i);//立刻執行該匿名函式,傳遞下標 i(實參)
}
解決辦法二
/*
解決思路:
將下標作為物件屬性(name:"i",value:i的值)新增到每個陣列項(p物件)中
*/
for(var i =0;i<arr.length;i++){