閉包的概念 轉

2022-01-30 18:59:46 字數 3613 閱讀 3923

首先,我覺得,乙個概念,如果不理解也不影響使用的話,那麼,就沒必要去理解它、去學習它。閉包就是這樣乙個概念,你不理解它也能很好的用它。俺這兩年寫as3程式,是天天在和它打交道,甚至有過乙個function套乙個,乙個方法中套了20多個function的極端例子,但從未深究過它是怎麼實現的,它就像水和空氣一樣,我們不需要知道水是h2o,空氣是氧氣氮氣二氧化碳等的混合物,也活的好好的。

其次,我覺得,網上對閉包概念的解釋都太狹隘了,看得人蛋疼,就像回到了i++,++i時代一樣。如果非要去理解這個概念,像那樣去理解,則收穫太小,不值得。

維基百科上對閉包的解釋就很經典:

在電腦科學中,閉包(closure)是詞法閉包(lexical closure)的簡稱,是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。

peter j. landin 在2023年將術語閉包定義為一種包含環境成分和控制成分的實體。

下面是我理解的閉包概念。

先看看數學上的閉包。

(1,5) 是乙個區間,但對這個區間做分析、計算什麼的,經常會用到1和5這兩個不屬於這個區間的值,[1,5]就是(1,5)的閉包。

在生活上,我們辦事情,找a部門,a部門說,你先得找b部門蓋個章,b部門說,你先得找c部門蓋個章,c部門說,這個東西不是我們的職權範圍…… 踢皮球,這就是非閉包。閉包就是負責到底,你找到a部門,a部門接待的那個人負責到底,他/她去協調b部門和c部門。

在工程上,閉包就是專案經理,負責排程專案所需要的資源。老闆、客戶有什麼事情,直接找專案經理即可,不用再去找其它的人。

在程式語言中,閉包就是一種語法糖,它以很自然的形式,把我們的目的和我們的目的所涉及的資源全給自動打包在一起,以某種自然、盡量不讓人誤解的方式讓人來使用。至於其具體實現,我個人意見,在不影響使用的情況下,不求甚解即可。在很多情況下,需要在一段**裡去訪問外部的區域性變數,不提供這種語法糖,需要寫非常多的**,有了閉包這個語法糖,就不用寫這麼多**,自然而然的就用了。

這樣一來,可以把閉包從乙個語法機制提公升為一種設計原則:

閉包是從使用者角度考慮的一種設計概念,它基於對上下文的分析,把齷齪的事情、複雜的事情和外部環境互動的事情都自己做了,留給使用者乙個很自然的介面。

在這個原則下,函式式語言中,那種所謂的閉包只是一種「閉包」,還有大量的其它型別的「閉包」等待發現和實現。

下面舉出一些閉包設計原則的正例和反例。

正例:flex中的資料繫結語法就是一種「閉包」。x="",對於這個語法,編譯器自動去上下文中尋找叫 b 和 c 的變數,然後再找他們內部 num 變數,如果他們都是可繫結的話,則自動給它們新增上繫結鏈,當 b, c, num 等有任一變動時,更新 x 的值。

反例:winform 中的設計就違反了閉包原則,當不是在該ui執行緒中,更新某些控制項的值時,會丟擲異常。只能去invoke呼叫,而invoke的介面很難用,相信很多人對這東東極其反感。

閉包不一定是語法糖。當我們不能直接擴充套件編譯器時,我們就無法增加語法糖來實現閉包機制,這時,就要用現有的語言機制來實現了。

下面,我們來對winform的invoke方法進行改造,使它滿足閉包原則。下面是**:

public class controlfunccontext 

public delegate delegate

public controlfunccontext(control ctl, delegate d) 

public void invoke0() 

catch(objectdisposedexception ex)  } 

}public void invoke1(t obj) 

catch (objectdisposedexception ex)  } 

}public void invoke2(t0 obj0, t1 obj1) 

catch (objectdisposedexception ex)  } 

} }public static class formclasshelper  }

public static void invokeaction(this control ctl, actionaction, t obj)  }

public static void invokeaction(this control ctl, actionaction, t0 obj0, t1 obj1)  } 

}

使用起來很簡單,直接呼叫擴充套件方法 invokeaction 即可,不必去考慮跨執行緒還是不跨執行緒這些「環境因素」,跨執行緒呼叫,我們已經通過使用者不必知曉的方式,把它封裝起來了。

再舉個例子,寫程式經常需要這樣乙個功能:開啟乙個影象檔案,然後進行處理。正常寫法很麻煩,比如,那個filter格式就很容易忘記,那麼,我們就把它閉包化,把不該讓使用者知道,不該讓使用者敲鍵盤的都給它封裝起來:

public static void openfile(this form element, actioncallbackonfilepath, string filter = "所有檔案|*.*")

string filepath;

openfiledialog dlg = new openfiledialog();

dlg.filter = filter;

dlg.fileok += (object sender, canceleventargs e) =>

filepath = dlg.filename;

if (callbackonfilepath != null)

callbackonfilepath(filepath);

dlg.showdialog();

public static void openimagefile(this form element, actioncallbackonfilepath, string filter = "影象檔案|*.bmp;*.jpg;*.gif;*.png")

openfile(element, callbackonfilepath, filter);

再舉乙個例子,這個例子是as3中的。在flex中,控制項有乙個calllater 方法,在下一幀時進行呼叫。這個方法非常有用,很多時候,非flex專案也需要這樣的乙個方法。下面,我們進行模擬:

package orc.utils 

private var stage:stage; 

private var callback:function; 

private function onstageenterframe(event:event):void  } 

} }

然後在基礎控制項中,提供calllater方法:

public function calllater(callback:function):void 

總結:

(1)閉包是一種設計原則,它通過分析上下文,來簡化使用者的呼叫,讓使用者在不知曉的情況下,達到他的目的;

(2)網上主流的對閉包剖析的文章實際上是和閉包原則反向而馳的,如果需要知道閉包細節才能用好的話,這個閉包是設計失敗的;

(3)盡量少學習。

閉包的概念

因此 可以訪問外部函式的變數,其內部變數只能內部可訪問 閉包時塊級作用域,可以定義自己的變數,避免變數命名衝突,汙染外部變數 使用場景一 封裝私有變數,對外暴露get,set方法或其中一種 使用場景二 儲存外部函式的變數 使用場景三 使用場景三 當閉包被賦值給乙個生命較長的變數時,其所依賴的父函式的...

閉包概念集合

一 建立閉包 建立閉包的常見方式,就是在乙個函式內部建立另乙個函式。二 作用域鏈 當某個函式被呼叫的時候,會建立乙個執行環境和相應的作用域鏈,然後使用arguments初始化物件。這個物件叫做活動物件。在作用域鏈中,外部函式的活動物件始終處於第二位。以此類推,直到作用域鏈終點 全域性執行環境。首先,...

js 閉包概念,用法

1 我們常說的閉包就是函式巢狀函式,內部函式可以引用外部函式的引數和變數 如下 2 會涉及到js中的垃圾 機制 指 當函式執行完時,函式的變數將會收回,閉包就不一樣了 如下 3 閉包中當外部函式載入完時,外部函式的變數內部函式還可以繼續使用,不會 如下 4 閉包的好處 將乙個變數長期駐紮在記憶體中 ...