物件導向的 Javascript 語言特性:閉包

來源:互聯網
上載者:User

  閉包

  閉包意味著內層的函數可以引用存在於包繞它的函數的變數,即使外層的函數的執行已經終止。這一特殊的論題可能是非常強大又非常複雜的。我強烈推薦你們參考本節後面將提及的網站,因為它有一些關於閉包這一話題的精彩的資訊。
  我們先來看程式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的物件導向特性賴以建立的基石。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.