當(dāng)柳上原的風(fēng)吹向天際的時(shí)候...真正的快樂來源于創(chuàng)造 |
閉包是JavaScript的復(fù)雜特性之一,搞清楚它有益于JS復(fù)雜程序的編寫和排錯(cuò)。什么是閉包呢?讓我們先看看《jQuery基礎(chǔ)教程》一書中對閉包的描述:
“當(dāng)內(nèi)部函數(shù)在定義它的作用于的外部被引用時(shí),就創(chuàng)建了該內(nèi)部函數(shù)的一個(gè)閉包。在這種情況下,我們稱布什內(nèi)部函數(shù)局部變量的變量為自由變量,稱外部函數(shù)的調(diào)用環(huán)境為封閉閉包的環(huán)境。從本質(zhì)上講,如果內(nèi)部函數(shù)引用了位于外部函數(shù)中的變量,相當(dāng)于授權(quán)該變量能夠被延遲使用。因此,當(dāng)外部函數(shù)調(diào)用完成后,這些變量的內(nèi)存不會(huì)被釋放,因?yàn)殚]包仍然需要使用它們。” 這段話解釋得很清楚了。如果再簡短一些,用我自己的話說,閉包=封閉+打包,它的作用是將內(nèi)部函數(shù)及其使用到的外部變量(內(nèi)部函數(shù)的作用環(huán)境)封閉在一起打包使用。具體過程請看下面的程序: <SCRIPT LANGUAGE="JavaScript">
<!-- // 定義兩個(gè)引用 var pointer1; var pointer2; // 外部函數(shù)定義 function outer(){ // 外部函數(shù)的局部變量 var i=0; // 在外部函數(shù)內(nèi)部定義的內(nèi)部函數(shù),使用了外部函數(shù)的局部變量i function inner(){ alert(i); i++; } // 將兩個(gè)引用指向內(nèi)部函數(shù)inner pointer1=inner; pointer2=inner; } outer(); alert("通過引用調(diào)用內(nèi)部函數(shù)開始"); pointer1();// 彈出0 pointer1();// 彈出1 pointer2();// 彈出2 pointer2();// 彈出3 //--> </SCRIPT> 執(zhí)行以上程序,很容易就發(fā)覺,內(nèi)部函數(shù)inner和外部函數(shù)定義的局部變量i被封閉并打包在一起了,就是閉包。這種方式和Java中向匿名類傳遞參數(shù)需要用final指定外部變量的做法異曲同工,都是改變局部變量的生存周期和作用范圍以配合特定函數(shù)的使用而已,不同的是Java在明JS在暗。 如果創(chuàng)建了內(nèi)部函數(shù)的多個(gè)副本,那么被打包的外部函數(shù)中的變量也會(huì)產(chǎn)生互不影響的多個(gè)副本,從下面的程序可以清晰的看出來: <SCRIPT LANGUAGE="JavaScript">
<!-- // 定義兩個(gè)引用 var pointer1; var pointer2; // 外部函數(shù)定義 function outer(){ // 外部函數(shù)的局部變量 var i=0; // 在外部函數(shù)內(nèi)部定義的內(nèi)部函數(shù),使用了外部函數(shù)的局部變量i function inner(){ alert(i); i++; } // 返回內(nèi)部函數(shù)的引用 return inner; } // 將引用指向內(nèi)部函數(shù) pointer1=outer(); pointer2=outer(); alert("通過引用調(diào)用內(nèi)部函數(shù)開始"); pointer1();// 彈出0 pointer1();// 彈出1 pointer2();// 彈出0 pointer2();// 彈出1 //--> </SCRIPT> 明顯,pointe1和pointer2指向的是inner的兩個(gè)副本,它們各自有不同的閉包環(huán)境,因此使用到的i也是從屬于各自閉包環(huán)境的兩個(gè)不同變量,它們地址,值都各不相同,相同的只有名字而已,就好像兩個(gè)一般函數(shù)中都有變量i一樣。 以上程序的完整代碼請見:http://www.aygfsteel.com/Files/heyang/closure20090823214249.rar JS閉包的這個(gè)特性對動(dòng)態(tài)創(chuàng)建的程序特別有效,如下圖中“刪除”按鈕的創(chuàng)建,就會(huì)用到閉包。 ![]() 請看代碼: // deleteImgCell
var deleteBtn=document.createElement("input"); deleteBtn.setAttribute("type","button"); deleteBtn.setAttribute("value","刪除"); deleteBtn.onclick=function(){deleteRow(code);};// 這里用到了閉包 var deleteBtnCell=document.createElement("td"); deleteBtnCell.appendChild(deleteBtn); deleteBtn.onclick=function(){deleteRow(code);};這句代碼為什么一定要用一個(gè)函數(shù)體包容一下呢?我們知道deleteRow這個(gè)函數(shù)的作用是刪除掉一個(gè)表格行,傳入的參數(shù)是行的id,而每行的id是不同的,程序中的id值來自于局部變量code,我們不希望它創(chuàng)建完按鈕后值發(fā)生變化而是希望它運(yùn)行時(shí)的值和每個(gè)按鈕的點(diǎn)擊相應(yīng)函數(shù)綁定在一起,用JS的閉包正好完成了這一點(diǎn)。 以上代碼的完整程序請見:http://www.aygfsteel.com/Files/heyang/StockTable.rar 在http://www.aygfsteel.com/heyang/archive/2009/08/21/292106.html 中的程序里也使用到了閉包,以下的程序: window.onload=function(){
var menubar=$("menubar"); for(var i=0;i<menubar.childNodes.length;i++){ new function(){// 這里使用了閉包,將局部變量和事件響應(yīng)函數(shù)打包封閉在了一起. var li=menubar.childNodes[i]; var subul=li.childNodes[2]; li.attachEvent('onmouseover', function(){ subul.style.display="block"; } ); li.attachEvent('onmouseout', function(){ subul.style.display="none"; } ); } } } 當(dāng)然,不要紅字部分的function也是一種閉包,只是打包封閉的環(huán)境錯(cuò)誤了而已. 以上代碼的完整程序請見:http://www.aygfsteel.com/Files/heyang/JSCSSPopupmenu20090821151741.rar
摘要: 程序員如果沒有美工的幫助只能在網(wǎng)上找些現(xiàn)成的圖片來用,比較費(fèi)事。我閑逛時(shí)偶遇一圖片做菜單背景不錯(cuò),于是作了兩個(gè)菜單,看起來比單色的還是漂亮不少。 閱讀全文
摘要: FormChecker是我在原來的表單驗(yàn)證器的基礎(chǔ)上改進(jìn)的類化版本,比原來的結(jié)構(gòu)要好些,使用上方便些。 閱讀全文
摘要: 更新了以前制作的JS類MyTable,有清除所所有行,取得行數(shù),添加行,刪除行和更新背景色等類函數(shù),并增加了創(chuàng)建文本單元格和創(chuàng)建鏈接單元格這兩個(gè)輔助函數(shù)。 閱讀全文
摘要: 一個(gè)使用CSS定義表格的例子,寫出來以備后用。 閱讀全文
摘要: JS+CSS彈出式二級菜單的改進(jìn)版,這次目的是將樣式,內(nèi)容和行為完全的分離。 閱讀全文
摘要: CSS+JavaScript彈出式二級菜單示例,很簡單。 閱讀全文
摘要: 本例將藍(lán)色緞帶標(biāo)題欄和內(nèi)容欄混合在一起。 閱讀全文
摘要: 一個(gè)漂亮的標(biāo)題欄能給人愉悅的感受,是時(shí)候告別單色或單圖片標(biāo)題欄了。 閱讀全文
|