閉包的定義非常晦澀——閉包,是指文法域位於某個特定的地區,具有持續參照(讀寫)位於該地區內自身範圍之外的執行域上的非持久型變數值能力的段落。這些外部執行域的非持久型變數神奇地保留它們在閉包最初定義(或建立)時的值(深連結)。簡單來說,閉包就是在另一個範圍中儲存了一份它從上一級函數或範圍取得的變數(索引值對),而這些索引值對是不會隨上一級函數的執行完成而銷毀。周愛民說得更清楚,閉包就是“屬性工作表”,閉包就是一個資料區塊,閉包就是一個存放著“Name=Value”的對照表。就這麼簡單。但是,必須強調,閉包是一個運行期概念。
在Javascript中閉包(Closure),有兩個特點:
作為一個函數變數的一個引用 - 當函數返回時,其處於啟用狀態。
一個閉包就是當一個函數返回時,一個沒有釋放資源的棧區。
現在比較讓人認同的閉包實現有如下三種
複製代碼 代碼如下:with(obj){
//這裡是對象閉包
}
複製代碼 代碼如下:(function(){
//函數閉包
})()
複製代碼 代碼如下:try{
//...
} catch(e) {
//catch閉包 但IE裡不行
}
幾個有用的樣本 複製代碼 代碼如下://*************閉包uniqueID*************
uniqueID = (function(){ //這個函數的調用對象儲存值
var id = 0; //這是私人恒久的那個值
//外層函數返回一個有權訪問恒久值的嵌套的函數
//那就是我們儲存在變數uniqueID裡的嵌套函數.
return function(){return id++;}; //返回,自加.
})(); //在定義後調用外層函數.
document.writeln(uniqueID()); //0
document.writeln(uniqueID()); //1
document.writeln(uniqueID()); //2
document.writeln(uniqueID()); //3
document.writeln(uniqueID()); //4
複製代碼 代碼如下://*************閉包階乘*************
var a = (function(n){
if(n<1){ alert("invalid arguments"); return 0; }
if(n==1){ return 1; }
else{ return n * arguments.callee(n-1); }
})(4);
document.writeln(a);
複製代碼 代碼如下:function User( properties ) {
//這裡一定要聲明一個變數來指向當前的instance
var objthis = this;
for ( var i in properties ) {
(function(){
//在閉包內,t每次都是新的,而 properties[i] 的值是for裡面的
var t = properties[i];
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {t = val;};
})();
}
}
//測試代碼
var user = new User({
name: "Bob",
age: 44
});
alert( user.getname());
alert( user.getage());
user.setname("Mike");
alert( user.getname());
alert( user.getage());
user.setage( 22 );
alert( user.getname());
alert( user.getage());
附上今天在無憂看到的問題:
要求:
讓這三個節點的Onclick事件都能正確的彈出相應的參數。 複製代碼 代碼如下:<ul>
<li id="a1">aa</li>
<li id="a2">aa</li>
<li id="a3">aa</li>
</ul>
<script type="text/javascript">
for(var i=1; i < 4; i++){
var id = document.getElementById("a" + i);
id.onclick = function(){
alert(i);//現在都是返回4
}
}
</script>
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
我的解釋是,onclick的綁定函數 function(){alert(i)}的範圍為對應li對象,它裡面alert的i的範圍為window,每次迴圈都是在重寫window.i的值,因此迴圈完,i已是4,點擊哪一個li元素都是4。
解決方案:
使用函數閉包。 複製代碼 代碼如下:var lists = document.getElementsByTagName("li");
for(var i=0,l=lists.length; i < l; i++){
lists[i].onclick = (function(i){//儲存於外部函函數
return function(){
alert(i);
}
})(i);
}
複製代碼 代碼如下:var lists = document.getElementsByTagName("li");
for(var i=0,l=lists.length; i < l; i++){
lists[i].onclick = new function(){
var t = i;
return function(){
alert(t+1)
}
}
}
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美(函數閉包)<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
利用事件代理 複製代碼 代碼如下:var ul = document.getElementsByTagName("ul")[0];
ul.onclick = function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target;
if(target.nodeName.toLowerCase() == "li"){
alert(target.id.slice(-1))
}
}
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美(事件代理)<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
將暫時變數保留於元素節點上。 複製代碼 代碼如下:var lists = document.getElementsByTagName("li");
for(var i=0,t=0,el; el = list[i++];){
el.i = t++
el.onclick = function(){
alert(this.i)
}
}
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美(將臨時變數變成某一個對象的屬性)<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
使用with語句造成的對象閉包。 複製代碼 代碼如下:var els = document.getElementsByTagName("li")
for(var i=0,n=els.length;i<n;i++){
with ({i:i})
els[i].onclick = function() { alert(this.innerHTML+i) };
}
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美(對象閉包)<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
使用try...catch語句構造的異常閉包: 複製代碼 代碼如下:var lists = document.getElementsByTagName("li");
for(var i=0,l=lists.length; i < l; i++){
try{
throw i;
}catch(i){
lists[i].onclick = function(){
alert(i)
}
}
}
<meta charset="utf-8"><meta name="keywords" content="javascript閉包 by 司徒正美"><meta name="description" content="javascript閉包 by 司徒正美"><br />javascript閉包 by 司徒正美(異常閉包)<ul><li id="a1">aa</li><li id="a2">aa</li><li id="a3">aa</li></ul><p>
[Ctrl+A 全選 注:如需引入外部Js需重新整理才能執行]
複製代碼 代碼如下:var els = document.getElementsByTagName("li");
(''+Array(els.length+1)).replace(/./g,function(a,i){
els[i].onclick=function(){alert(i)}
})