成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

閉包

ccj659 / 2853人閱讀

摘要:閉包在計算機科學(xué)中,閉包是詞法閉包的簡稱,是引用了自由變量的函數(shù)。所以,有另一種說法認為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。通過閉包完成了私有的成員和方法的封裝。

閉包
  

在計算機科學(xué)中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說法認為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。 --- 維基百科

其實這段引用已經(jīng)說明了閉包的本質(zhì):引用了自由變量的函數(shù)自由變量將和這個函數(shù)一同存在——這是理解閉包的關(guān)鍵。

一 原理解釋

函數(shù)式編程語言的基礎(chǔ)是lambda演算,而閉包又是從函數(shù)式編程衍生而來。下面先從Lambda演算理解下函數(shù)式思維。(不感興趣可直接跳過

Lambda演算
  

Lambda演算是一套用于研究函數(shù)定義、應(yīng)用和遞歸的形式系統(tǒng)。它包括一條變換規(guī)則(變量替換)和一條函數(shù)定義方式,Lambda演算之通用在于,任何一個可計算函數(shù)都能用這種形式來表達和求值。因而,它是等價于圖靈機的。盡管如此,Lambda演算強調(diào)的是變換規(guī)則的運用,而非實現(xiàn)它們的具體機器??梢哉J為這是一種更接近軟件而非硬件的方式。Lambda演算對函數(shù)式編程語言有巨大的影響,比如Lisp和Haskell。

非形式化的描述
  

在lambda演算中,每個表達式都代表一個函數(shù),這個函數(shù)有一個參數(shù),并且返回一個值。不論是參數(shù)和返回值,也都是一個單參的函數(shù)。可以這么說,lambda演算中,只有一種“類型”,那就是這種單參函數(shù)。

:在函數(shù)式編程語言中,函數(shù)可是一等公民。

函數(shù)是通過λ表達式匿名地定義的,這個表達式說明了此函數(shù)將對其參數(shù)進行什么操作。例如,“加2”函數(shù)f(x)= x + 2可以用lambda演算表示為λx.x + 2 (或者λy.y + 2,參數(shù)的取名無關(guān)緊要)

λ演算中函數(shù)只有一個參數(shù),那有兩個參數(shù)的函數(shù)怎么表達呢?可以通過lambda演算這么表達:一個單一參數(shù)的函數(shù)的返回值又是一個單一參數(shù)的函數(shù)閉包嗎?)。
例如,函數(shù)f(x, y) = x + y可以寫作:

λx.λy.x + y  ----->λx. (λ y. + x y)

上面這個轉(zhuǎn)化就叫currying,它展示了,我們?nèi)绾螌崿F(xiàn)加法(假設(shè)+這個符號已經(jīng)具有相加的功能)。

其實就是我們現(xiàn)在意義上的閉包——你調(diào)用一個函數(shù),這個函數(shù)返回另一個函數(shù),返回的函數(shù)中存儲保留了調(diào)用函數(shù)的變量。currying是閉包的鼻祖。(如果理解困難,下面會用編程語言實現(xiàn)上面的演算)

閉包解釋

閉包被廣泛使用于函數(shù)式編程語言,慢慢很多命令式語言也開始支持閉包。在函數(shù)中可以(嵌套)定義另一個函數(shù)時,如果內(nèi)部的函數(shù)引用了外部的函數(shù)的變量,則可能產(chǎn)生閉包。運行時,一旦外部的函數(shù)被執(zhí)行,一個閉包就形成了,閉包中包含了內(nèi)部函數(shù)的代碼,以及所需外部函數(shù)中的變量的引用。

典型的支持閉包的語言中,通常將函數(shù)當作第一類對象——在這些語言中,函數(shù)通常有下列特性:
- 可以將函數(shù)賦值給一個變量
- 函數(shù)可以作為參數(shù)傳遞
- 函數(shù)的返回值可以是一個函數(shù)

例如以下Scheme(Lisp的一個方言)代碼:

(define (f x) (lambda (y) (+ x y)))

在這個例子中,lambda表達式(lambda (y) (+ x y))出現(xiàn)在函數(shù)f中。當這個lambda表達式被執(zhí)行時,Scheme創(chuàng)造了一個包含此表達式以及對x變量的引用的閉包,其中x變量在lambda表達式中是自由變量。

下面是用ECMAScript (JavaScript)寫的同一個例子:

function f(x){ 
    return function(y) { 
        return x + y; 
        }; 
    }

其中f返回的匿名函數(shù)與其自由變量x組成了一個閉包。

上述代碼在nodeJS環(huán)境中執(zhí)行:

console.log( (f(7)) (2) );
//9

首先用第一個參數(shù)(7)代替最外層函數(shù)的參數(shù)(x),然后用第二個參數(shù)(2)代替第二層函數(shù)的參數(shù)(y),最終得到計算結(jié)果。

注意:這個運算執(zhí)行了兩個函數(shù):f匿名函數(shù)。f的作用域為(f 7),這就是說,當(f 7)執(zhí)行后,f這個函數(shù)就結(jié)束了,而x是f的私有變量,理論上x應(yīng)該被釋放了,然后x在f函數(shù)執(zhí)行結(jié)束后并沒有被釋放,而是繼續(xù)被匿名函數(shù)繼續(xù)使用。支持這種機制的語言稱為支持閉包機制在函數(shù)中可以(嵌套)定義另一個函數(shù)時,如果內(nèi)部的函數(shù)引用了外部函數(shù)的變量,即使已經(jīng)離開了外部函數(shù)的環(huán)境,自由變量(外部函數(shù)的變量)也和內(nèi)部函數(shù)一同存在,則產(chǎn)生閉包)。

二 閉包的實現(xiàn)

通過上面的原理解釋我們提出了這樣一個問題(雖然這樣的問題不干擾你理解閉包):

  

如果一個函數(shù)定義在棧中,那么當函數(shù)返回時,定義在函數(shù)中的局部變量就不復(fù)存在了, 那為什么內(nèi)部的函數(shù)可以訪問外部函數(shù)的變量?即使外部函數(shù)執(zhí)行完,外部函數(shù)的變量也能和內(nèi)部函數(shù)一同存在?

下面以JavaScript閉包實現(xiàn)舉例。

javascript

上面是一個使用閉包的簡單示例,代碼執(zhí)行完畢后,函數(shù)對象并不會被垃圾回收機制回收1,函數(shù)內(nèi)的臨時變量能夠得以長期存在,而這個變量只能夠被閉包函數(shù)修改,在外部是無法訪問和修改的。(這個其實前面已經(jīng)說過,這里有點啰嗦)

JavaScript中將作用域鏈描述為一個對象列表,不是綁定的棧。每次調(diào)用JavaScript函數(shù)的時候(函數(shù)也是對象),都會為之創(chuàng)建一個新的對象用來保存局部變量,把這個對象添加至作用域鏈中。

每個函數(shù)關(guān)聯(lián)都有一個執(zhí)行上下文場景(Execution Context) ,然后執(zhí)行環(huán)境會創(chuàng)建一個活動對象(call object),該對象包含了兩個重要組件,環(huán)境記錄,和外部引用(指針)。環(huán)境記錄包含了函數(shù)內(nèi)部聲明的局部變量和參數(shù)變量,外部引用指向了外部函數(shù)對象的上下文執(zhí)行場景。這樣的數(shù)據(jù)結(jié)構(gòu)就構(gòu)成了一個單向的鏈表,每個引用都指向外層的上下文場景。最后形成如下圖的結(jié)構(gòu):

注:此圖盜用,此圖網(wǎng)站已不能打開。

如上圖和代碼所示:a返回函數(shù)b的引用給c,函數(shù)b的作用域鏈包含了對函數(shù)a的活動對象的引用,也就是說b可以訪問到a中定義的所有變量和函數(shù)。函數(shù)b被c引用,函數(shù)b又依賴函數(shù)a,因此函數(shù)a在返回后不會被GC回收。

三 閉包的作用

以上述代碼為例:
- 保護函數(shù)內(nèi)的變量安全:函數(shù)a中i只有函數(shù)b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全;
- 在內(nèi)存中維持一個變量:函數(shù)a中i的一直存在于內(nèi)存中,因此每次執(zhí)行c(),都會給i自加1;
- 通過保護變量的安全實現(xiàn)JS私有屬性和私有方法(不能被外部訪問)。

Singleton 單件:(盜用理解Javascript的閉包的例子)

var singleton = function () {
    var privateVariable;
    function privateFunction(x) {
        ...privateVariable...
    }

    return {
        firstMethod: function (a, b) {
            ...privateVariable...
        },
        secondMethod: function (c) {
            ...privateFunction()...
        }
    };
}();

這個單件通過閉包來實現(xiàn)。通過閉包完成了私有的成員和方法的封裝。匿名主函數(shù)返回一個對象。對象包含了兩個方法,方法1可以方法私有變量,方法2訪問內(nèi)部私有函數(shù)。需要注意的地方是匿名主函數(shù)結(jié)束的地方的"()’,如果沒有這個"()’就不能產(chǎn)生單件。因為匿名函數(shù)只能返回了唯一的對象,而且不能被其他地方調(diào)用。這個就是利用閉包產(chǎn)生單件的方法。

四 概念混淆之匿名函數(shù)

匿名函數(shù)指的是沒有函數(shù)名稱的函數(shù),它和閉包沒有關(guān)系,只是閉包中函數(shù)可以通過匿名函數(shù)編寫,當然匿名函數(shù)不止會出現(xiàn)在閉包中。
如下面一個javascript代碼示例:

var f = function(name){ 
    //函數(shù)體 
};

函數(shù)表達式是創(chuàng)建了一個匿名函數(shù),然后將匿名函數(shù)賦值給一個變量f。

參考

理解Javascript的閉包
閉包漫談(從抽象代數(shù)及函數(shù)式編程角度)
維基百科-閉包
維基百科-λ演算
編程語言的基石——Lambda calculus

腳注

注:Javascript的垃圾回收機制:
在Javascript中,如果一個對象不再被引用,那么這個對象就會被GC回收。如果兩個對象互相引用,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收。因為函數(shù)a被b引用,b又被a外的c引用,這就是為什么函數(shù)a執(zhí)行后不會被回收的原因。??

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85785.html

相關(guān)文章

  • JS 中的閉包是什么?

    摘要:大名鼎鼎的閉包面試必問。閉包的作用是什么??吹介]包在哪了嗎閉包到底是什么五年前,我也被這個問題困擾,于是去搜了并總結(jié)下來。關(guān)于閉包的謠言閉包會造成內(nèi)存泄露錯。閉包里面的變量明明就是我們需要的變量,憑什么說是內(nèi)存泄露這個謠言是如何來的因為。 本文為饑人谷講師方方原創(chuàng)文章,首發(fā)于 前端學(xué)習(xí)指南。 大名鼎鼎的閉包!面試必問。請用自己的話簡述 什么是「閉包」。 「閉包」的作用是什么。 首先...

    Enlightenment 評論0 收藏0
  • 閉包,又見閉包。。。。?

    摘要:完美的閉包,對,閉包就這么簡單。這僅僅是閉包的一部分,閉包利用函數(shù)作用域達到了訪問外層變量的目的。此時一個完整的閉包實現(xiàn)了,的垃圾回收機制由于閉包的存在無法銷毀變量。 1.閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。 上面這段話來自 javascript 高級程序設(shè)計 第三版 P178 。作者說閉包是一個函數(shù),它有訪問另一個函數(shù)作用域中的變量的能力。 2.函數(shù)訪問它被創(chuàng)建時所處的...

    keelii 評論0 收藏0
  • 多層級理解閉包

    摘要:第二梯隊理解有了第一梯隊的認識,我們慢慢修正大腦中對閉包的認識。理解這句話就可以很好的與閉包這兩個字關(guān)聯(lián)起來理解閉包這個概念了??偨Y(jié)第二梯隊理解閉包是一個有特定功能的函數(shù)。第四梯隊理解閉包通過訪問外部變量,一個閉包可以維持這些變量。 閉包 閉包的概念困惑了我很久,記得當時我面試的時候最后一面有一個問題就是問題關(guān)于閉包的問題,然而到現(xiàn)在已經(jīng)完全不記得當時的題目是啥了,但仍然能夠回憶起當時...

    nemo 評論0 收藏0
  • 面試官問我:什么是JavaScript閉包,我該如何回答

    摘要:到底什么是閉包這個問題在面試是時候經(jīng)常都會被問,很多小白一聽就懵逼了,不知道如何回答好。上面這么說閉包是一種特殊的對象。閉包的注意事項通常,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀。從而使用閉包模塊化代碼,減少全局變量的污染。 閉包,有人說它是一種設(shè)計理念,有人說所有的函數(shù)都是閉包。到底什么是閉包?這個問題在面試是時候經(jīng)常都會被問,很多小白一聽就懵逼了,不知道如何回答好。這個...

    BenCHou 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<