面向對象的 Javascript 閉包

面向對象的 Javascript 閉包 收藏 閉包   閉包意味着內層的函數可以引用存在於包繞它的函數的變量,即使外層的函數的執行已經終止。這一特殊的論題可能是非常強大又非常複雜的。我強烈推薦你們參考本節後面將提及的站點,因爲它有一些關於閉包這一話題的精彩的信息。   我們先來看程序2-13所示的閉包的兩個簡單例子。      程序2-13. 閉包改善的代碼清晰性的兩例 //得到id爲"main"的元素 var obj = document.getElementById("main"); //改變它的邊框樣式 obj.style.border = "1px solid red"; //初始化一個1秒鐘以後被調用的回調函數 setTimeout(function(){ //此函數將隱藏該元素 obj.style.display = 'none'; }, 1000); //用來延遲顯示消息的通用函數 function delayedAlert( msg, time ) { //初始化一個被封套的函數 setTimeout(function(){ //此函數使用了來自封套它的函數的變量msg alert( msg ); }, time ); } //調用函數delayedAlert,帶兩個參數 delayedAlert( "Welcome!", 2000 );   第一個對setTimeout的函數調用,展示了一個的JavaScript新手遇到問題的通俗的例子。在JavaScript新手的程序裏像這樣的代碼時常可以看到: setTimeout("otherFunction()", 1000); //或者甚至 setTimeout("otherFunction(" + num + "," + num2 + ")", 1000);   使用閉包的概念,完全可能的把這種混亂的代碼清理掉。第一個例子很簡單;有一個回調函數在調用setTimeout函數以後1000微秒以後被調用,而它仍引用了變量obj(定義在全局範圍,指向id爲"main"的元素)。定義的第二個函數,delayedAlert,展示了一種解決出現的 setTimeout混亂的方案,以及函數作用域內可以有閉包的能力。   你們應該可以發現,當在代碼中使用這種簡單的閉包時,你所寫的東西的清晰性將會提高,免於陷入語法的迷霧之中。   我們來看一個閉包可能帶來的有有趣的副作用。在某些函數化的編程語言裏,有一個叫做currying 的概念。本質上講,currying是就是爲函數的一些參數預填入值,創建一個更簡單的新函數的方法。代碼2-14裏有一個簡單的currying的例子,創建了向另一個函數預填一個參數而得的新函數。   代碼2-14. 使用閉包的函數currying //生成做加法的新函數的函數 function addGenerator( num ) { //返回一個簡單函數用來計算兩個數的加法, //其中第一個數字從生成器中借用 return function( toAdd ) { return num + toAdd }; } //addFive現在是接受一個參數的函數, //此函數將給參數加5,返回結果數字 var addFive = addGenerator( 5 ); //這裏我們可以看到,當傳給它參數4的時候 //函數addFive的結果爲9 alert( addFive( 4 ) == 9 );   閉包還能解決另一個常見的JavaScript編碼方面的問題。JavaScript新手趨向於在全局作用域裏放置許多變量。這一般被認爲是不好的習慣,因爲那些變量可能悄悄地影響其它的庫,導致令人迷惑的問題的產生。使用一個自執行的、匿名的函數,你可以從根本上隱藏所有的通常的全局變量,使它們對其它代碼不可見,如程序2-15所示。      代碼2-15. 使用匿名函數從全局作用域隱藏變量的例子 //創建一個用作包裝的匿名函數 (function(){ //這個變量通常情況下應該是全局的 var msg = "Thanks for visiting!"; //爲全局對象綁定新的函數 window.onunload = function(){ //使用了“隱藏”的變量 alert( msg ); }; //關閉匿名函數並執行之 })();   最後,讓我們來看使用閉包時出現的一個問題。閉包允許你引用存在於父級函數中的變量。然而,它並不是提供該變量創建時的值;它提供的是父級函數中該變量最後的值。你會看到這個問題最通常是在一個for循環中。有一個變量被用作迭代器(比如i),在for內部新的函數被創建,並使用了閉包來引用該迭代器。問題是,當新的閉包函數被調用時,它們將會引用該iterator最後的值(比如,一個數組的最後位置),而不是你所期望的那個。程序2-16的例子說明,使用匿名函數激發作用域,在其中創建一個合乎期望的閉包是可能的。   程序2-16. 使用匿名函數激發一個創建多個閉包函數所需的作用域的例子 //id爲"main"的一個元素 var obj = document.getElementById("main"); //用來綁定的items數組 var items = [ "click", "keypress" ]; //遍歷items中的每一項 for ( var i = 0; i < items.length; i++ ) { //用自執行的匿名函數來激發作用域 (function(){ //在些作用域內存儲值 var item = items[i]; //爲obj元素綁定函數 obj[ "on" + item ] = function() { //item引用一個父級的變量, //該變量在此for循環的上文中已被成功地scoped(?) alert( "Thanks for your " + item ); }; })(); }   閉包的概念並非輕易可以掌握的;我着實花了大量的時間和精力才徹底弄清閉包有多麼強大。幸運的是,有一個精彩的資源解釋了JavaScript 中的閉包是怎麼工作的:Jim Jey的"JavaScript閉包",網址是http://jibbering.com/faq/faq_notes/closures.html。   最後,我們將研究上下文的概念,這是許多JavaScript的面向對象特性賴以建立的基石。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章