閉包
閉包意味著內(nèi)層的函數(shù)可以引用存在于包繞它的函數(shù)的變量,即使外層的函數(shù)的執(zhí)行已經(jīng)終止。這一特殊的論題可能是非常強(qiáng)大又非常復(fù)雜的。我強(qiáng)烈推薦你們參考本節(jié)后面將提及的站點(diǎn),因?yàn)樗幸恍╆P(guān)于閉包這一話題的精彩的信息。 我們先來看程序2-13所示的閉包的兩個(gè)簡單例子。 程序2-13. 閉包改善的代碼清晰性的兩例
//得到id為"main"的元素 var obj = document.getElementById("main");
//改變它的邊框樣式 obj.style.border = "1px solid red";
//初始化一個(gè)1秒鐘以后被調(diào)用的回調(diào)函數(shù) setTimeout(function(){ //此函數(shù)將隱藏該元素 obj.style.display = 'none'; }, 1000);
//用來延遲顯示消息的通用函數(shù) function delayedAlert( msg, time ) { //初始化一個(gè)被封套的函數(shù) setTimeout(function(){ //此函數(shù)使用了來自封套它的函數(shù)的變量msg alert( msg ); }, time ); }
//調(diào)用函數(shù)delayedAlert,帶兩個(gè)參數(shù) delayedAlert( "Welcome!", 2000 );
第一個(gè)對setTimeout的函數(shù)調(diào)用,展示了一個(gè)的JavaScript新手遇到問題的通俗的例子。在JavaScript新手的程序里像這樣的代碼時(shí)?梢钥吹剑
setTimeout("otherFunction()", 1000);
//或者甚至 setTimeout("otherFunction(" + num + "," + num2 + ")", 1000);
使用閉包的概念,完全可能的把這種混亂的代碼清理掉。第一個(gè)例子很簡單;有一個(gè)回調(diào)函數(shù)在調(diào)用setTimeout函數(shù)以后1000微秒以后被調(diào)用,而它仍引用了變量obj(定義在全局范圍,指向id為"main"的元素)。定義的第二個(gè)函數(shù),delayedAlert,展示了一種解決出現(xiàn)的setTimeout混亂的方案,以及函數(shù)作用域內(nèi)可以有閉包的能力。 你們應(yīng)該可以發(fā)現(xiàn),當(dāng)在代碼中使用這種簡單的閉包時(shí),你所寫的東西的清晰性將會提高,免于陷入語法的迷霧之中。 我們來看一個(gè)閉包可能帶來的有有趣的副作用。在某些函數(shù)化的編程語言里,有一個(gè)叫做currying的概念。本質(zhì)上講,currying是就是為函數(shù)的一些參數(shù)預(yù)填入值,創(chuàng)建一個(gè)更簡單的新函數(shù)的方法。代碼2-14里有一個(gè)簡單的currying的例子,創(chuàng)建了向另一個(gè)函數(shù)預(yù)填一個(gè)參數(shù)而得的新函數(shù)。
代碼2-14. 使用閉包的函數(shù)currying
//生成做加法的新函數(shù)的函數(shù) function addGenerator( num ) { //返回一個(gè)簡單函數(shù)用來計(jì)算兩個(gè)數(shù)的加法, //其中第一個(gè)數(shù)字從生成器中借用 return function( toAdd ) { return num + toAdd }; }
//addFive現(xiàn)在是接受一個(gè)參數(shù)的函數(shù), //此函數(shù)將給參數(shù)加5,返回結(jié)果數(shù)字 var addFive = addGenerator( 5 );
//這里我們可以看到,當(dāng)傳給它參數(shù)4的時(shí)候 //函數(shù)addFive的結(jié)果為9 alert( addFive( 4 ) == 9 );
閉包還能解決另一個(gè)常見的JavaScript編碼方面的問題。JavaScript新手趨向于在全局作用域里放置許多變量。這一般被認(rèn)為是不好的習(xí)慣,因?yàn)槟切┳兞靠赡芮那牡赜绊懫渌膸,?dǎo)致令人迷惑的問題的產(chǎn)生。使用一個(gè)自執(zhí)行的、匿名的函數(shù),你可以從根本上隱藏所有的通常的全局變量,使它們對其它代碼不可見,如程序2-15所示。 代碼2-15. 使用匿名函數(shù)從全局作用域隱藏變量的例子
//創(chuàng)建一個(gè)用作包裝的匿名函數(shù) (function(){ //這個(gè)變量通常情況下應(yīng)該是全局的 var msg = "Thanks for visiting!";
//為全局對象綁定新的函數(shù) window.onunload = function(){ //使用了“隱藏”的變量 alert( msg ); };
//關(guān)閉匿名函數(shù)并執(zhí)行之 })();
最后,讓我們來看使用閉包時(shí)出現(xiàn)的一個(gè)問題。閉包允許你引用存在于父級函數(shù)中的變量。然而,它并不是提供該變量創(chuàng)建時(shí)的值;它提供的是父級函數(shù)中該變量最后的值。你會看到這個(gè)問題最通常是在一個(gè)for循環(huán)中。有一個(gè)變量被用作迭代器(比如i),在for內(nèi)部新的函數(shù)被創(chuàng)建,并使用了閉包來引用該迭代器。問題是,當(dāng)新的閉包函數(shù)被調(diào)用時(shí),它們將會引用該iterator最后的值(比如,一個(gè)數(shù)組的最后位置),而不是你所期望的那個(gè)。程序2-16的例子說明,使用匿名函數(shù)激發(fā)作用域,在其中創(chuàng)建一個(gè)合乎期望的閉包是可能的。
程序2-16. 使用匿名函數(shù)激發(fā)一個(gè)創(chuàng)建多個(gè)閉包函數(shù)所需的作用域的例子
//id為"main"的一個(gè)元素 var obj = document.getElementById("main");
//用來綁定的items數(shù)組 var items = [ "click", "keypress" ];
//遍歷items中的每一項(xiàng) for ( var i = 0; i < items.length; i++ ) { //用自執(zhí)行的匿名函數(shù)來激發(fā)作用域 (function(){ //在些作用域內(nèi)存儲值 var item = items[i]; //為obj元素綁定函數(shù) obj[ "on" + item ] = function() { //item引用一個(gè)父級的變量, //該變量在此for循環(huán)的上文中已被成功地scoped(?) alert( "Thanks for your " + item ); }; })(); }
閉包的概念并非輕易可以掌握的;我著實(shí)花了大量的時(shí)間和精力才徹底弄清閉包有多么強(qiáng)大。幸運(yùn)的是,有一個(gè)精彩的資源解釋了JavaScript中的閉包是怎么工作的:Jim Jey的"JavaScript閉包",網(wǎng)址是http://jibbering.com/faq/faq_notes/closures.html。 最后,我們將研究上下文的概念,這是許多JavaScript的面向?qū)ο筇匦再囈越⒌幕?/p>
出處:藍(lán)色理想
責(zé)任編輯:moby
上一頁 語言特性:作用域 下一頁 語言特性:上下文
◎進(jìn)入論壇網(wǎng)頁制作、網(wǎng)站綜合版塊參加討論
|