亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

閉包

ccj659 / 2895人閱讀

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

閉包
  

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

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

一 原理解釋

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

Lambda演算
  

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

非形式化的描述
  

在lambda演算中,每個(gè)表達(dá)式都代表一個(gè)函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù),并且返回一個(gè)值。不論是參數(shù)和返回值,也都是一個(gè)單參的函數(shù)??梢赃@么說(shuō),lambda演算中,只有一種“類型”,那就是這種單參函數(shù)。

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

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

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

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

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

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

閉包解釋

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

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

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

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

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

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

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

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

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

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

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

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

二 閉包的實(shí)現(xiàn)

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

  

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

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

javascript

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

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

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

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

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

三 閉包的作用

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

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

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

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

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

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

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

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

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

參考

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

腳注

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/85785.html

相關(guān)文章

  • JS 中的閉包是什么?

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

    Enlightenment 評(píng)論0 收藏0
  • 閉包,又見(jiàn)閉包。。。。?

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

    keelii 評(píng)論0 收藏0
  • 多層級(jí)理解閉包

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

    nemo 評(píng)論0 收藏0
  • 面試官問(wèn)我:什么是JavaScript閉包,我該如何回答

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

    BenCHou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<