中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

go語言閉包的知識點整理

發布時間:2021-08-27 08:59:12 來源:億速云 閱讀:176 作者:chen 欄目:編程語言

這篇文章主要講解了“go語言閉包的知識點整理”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“go語言閉包的知識點整理”吧!

go閉包

Golang 中,閉包是一個可以引用其作用域之外變量的函數。
換句話說,閉包是一個內部函數,它可以訪問創建它的范圍內的變量。即使外部函數完成執行并且作用域被破壞,依然可以訪問。
在深入研究閉包之前,需要了解什么是匿名函數。

匿名函數

顧名思義,匿名函數就是沒有名字的函數。
舉個例子,我們創建一個一般函數和匿名函數

package mainimport (
    "fmt")func sayhi1() { // 一般函數
    fmt.Println("hello golang, I am Regular function")}func main() {
    sayhi1()
    sayhi2 := func() { //匿名函數
        fmt.Println("hello golang, I am Anonymous function")
    }
    sayhi2()}

結果輸出:

hello golang, I am Regular functionhello golang, I am Anonymous function

通過創建一個返回函數的函數來使用匿名函數。

package mainimport (
    "fmt")func sayhello(s string) func() {
    return func() {
        fmt.Println("hello", s)
    }}func main() {
    sayhello := sayhello("golang")
    sayhello()}

結果輸出:

hello golang

常規函數和匿名函數唯一區別是匿名函數不是在包級別聲明的,它們被動態地聲明,通常要么被使用,要么被遺忘,要么被分配給一個變量供以后使用。

閉包的本質

閉包是包含自由變量的代碼塊,這些變量不在這個代碼塊內或者任何全局上下文中定義,而是在定義代碼塊的環境中定義。由于自由變量包含在代碼塊中,所以只要閉包還被使用,那么這些自由變量以及它們引用的對象就不會被釋放,要執行的代碼為自由變量提供綁定的計算環境。
閉包的價值在于可以作為函數對象或者匿名函數,對于類型系統而言,這意味著不僅要表示數據還要表示代碼。支持閉包的多數語言都將函數作為第一級對象,就是說這些函數可以存儲到變量中作為參數傳遞給其他函數,最重要的是能夠被函數動態創建和返回。

Golang中的閉包同樣也會引用到函數外的變量,閉包的實現確保只要閉包還被使用,那么被閉包引用的變量會一直存在。從形式上看,匿名函數都是閉包。
我們來看個閉包例子:

package mainimport (
    "fmt")func caller() func() int {
    callerd := 1
    sum := 0
    return func() int {
        sum += callerd        return sum    }}func main() {
    next := caller()
    fmt.Println(next())
    fmt.Println(next())
    fmt.Println(next())}

結果輸出:

1
2
3

該例子中called 和sum 是自由變量,caller函數返回的匿名函數為自由變量提供了計算環境,匿名函數和自由變量組成的代碼塊其實就是閉包。在閉包函數中,只有匿名函數才能訪問自由變量called和sum,而無法通過其他途徑訪問,因此閉包自由變量的安全性。
按照命令式語言的規則,caller函數只是返回了匿名函數的地址,但在執行匿名函數時將會由于在其作用域內找不到sum和called變量而出錯。而在函數式語言中,當內嵌函數體內引用到體外的變量時,將會把定義時涉及到的引用環境和函數體打包成一個整體(閉包)返回。閉包的使用和正常的函數調用沒有區別。

現在我們給出引用環境的定義:在程序執行中的某個點所有處于活躍狀態的約束所組成的集合,其中的約束指的是一個變量的名字和其所代表的對象之間的聯系。
所以我們說:閉包=函數+引用環境

其實我們可以將閉包函數看成一個類(C++),一個閉包函數調用就是實例化一個對象,閉包的自由變量就是類的成員變量,閉包函數的參數就是類的函數對象的參數。在該例子中,next可以看作是實例化的一個對象,next()可以看做是對象函數調用的返回值。
這讓我們想起了一句名言:對象是附有行為的數據,而閉包是附有數據的行為

閉包使用的一些例子

  • 利用閉包實現數據隔離
    假設你想創建一個函數,該函數可以訪問即使在函數退出后仍然存在的數據。舉個例子,如果你想統計函數被調用的次數,但不希望其他任何人訪問該數據(這樣他們就不會意外更改它),你就可以用閉包來實現它:

package mainimport (
    "fmt")func caller() func() int {
    callerd := 0
    return func() int {
        callerd++
        return callerd    }}func main() {
    next := caller()
    fmt.Println(next())
    fmt.Println(next())
    fmt.Println(next())}

結果輸出:

1
2
3
  • 利用閉包包裝函數和創建中間件
    Go 中的函數是一等公民。這意味著您不僅可以動態創建匿名函數,還可以將函數作為參數傳遞給函數。例如,在創建 Web 服務器時,通常會提供一個功能來處理Web 請求到特定的路由。

package mainimport (
  "fmt"
  "net/http")func main() {
  http.HandleFunc("/hello", hello)
  http.ListenAndServe(":3000", nil)}func hello(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "<h2>Hello!</h2>")}

在上面例子中,函數 hello() 被傳遞給 http.HandleFunc() 函數,并在該路由匹配時調用。
雖然這段代碼不需要閉包,但如果我們想用更多邏輯包裝我們的處理程序,閉包是非常有用的。一個完美的例子是我們可以通過創建中間件來在我們處理程序執行之前或之后做一些其它的工作。
什么是中間件?
中間件基本上是可重用功能的一個奇特術語,它可以在設計用于處理 Web 請求的代碼之前和之后運行代碼。在 Go 中,這些通常是通過閉包來實現的,但在不同的編程語言中,可以通過其他方式來實現。
在編寫 Web 應用程序時使用中間件很常見,而且它們不僅可用于計時器(您將在下面看到一個示例)。例如,中間件可用于編寫代碼驗證用戶是否登錄過一次,然后將其應用到你的所有會員專頁。
讓我們看看一個簡單的計時器中間件在 Go 中是如何工作的。

package mainimport (
  "fmt"
  "net/http"
  "time")func main() {
  http.HandleFunc("/hello", timed(hello))
  http.ListenAndServe(":3000", nil)}func timed(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
  return func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    f(w, r)
    end := time.Now()
    fmt.Println("The request took", end.Sub(start))
  }}func hello(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "<h2>Hello!</h2>")}

timed() 函數接受一個可以用作處理函數的函數,并返回一個相同類型的函數,但返回的函數與傳遞它的函數不同。返回的閉包記錄當前時間,調用原始函數,最后記錄結束時間并打印出請求的持續時間。同時對我們的處理程序函數內部實際發生的事情是不可知的。
現在我們要做的就是將我們的處理程序包裝在 timed(handler) 中并將閉包傳遞給 http.HandleFunc() 函數調用。

  • 利用閉包使用 sort 二分搜索
    使用標準庫中的包也經常需要閉包,例如 sort 包。這個包為我們提供了大量有用的功能和代碼,用于排序和搜索排序列表。例如,如果您想對一個整數切片進行排序,然后在切片中搜索數字 7,您可以像這樣使用 sort 包。

package mainimport (
  "fmt"
  "sort")func main() {
  numbers := []int{1, 11, -5, 7, 2, 0, 12}
  sort.Ints(numbers)
  fmt.Println("Sorted:", numbers)
  index := sort.SearchInts(numbers, 7)
  fmt.Println("7 is at index:", index)}

結果輸出:

Sorted: [-5 0 1 2 7 11 12]7 is at index: 4

如果要搜索的每個元素都是自定義類型的切片會發生什么?或者,如果您想找到第一個等于或大于 7 的數字的索引,而不僅僅是 7 的第一個索引?
為此,您可以使用 sort.Search() 函數,并且您需要傳入一個閉包,該閉包可用于確定特定索引處的數字是否符合您的條件。
sort.Search() is a binary search
sort.Search 函數執行二分搜索,因此它需要一個閉包,該閉包在滿足您的條件之前對任何索引返回 false,在滿足后返回 true。
讓我們使用上面描述的示例來看看它的實際效果;我們將搜索列表中第一個大于或等于 7 的數字的索引。

package mainimport (
    "fmt"
    "sort")func main() {
    numbers := []int{1, 11, -5, 8, 2, 0, 12}
    sort.Ints(numbers)
    fmt.Println("Sorted:", numbers)

    index := sort.Search(len(numbers), func(i int) bool {
        return numbers[i] >= 7
    })
    fmt.Println("The first number >= 7 is at index:", index)
    fmt.Println("The first number >= 7 is:", numbers[index])}

結果輸出:

Sorted: [-5 0 1 2 8 11 12]The first number >= 7 is at index: 4
The first number >= 7 is: 8

在這個例子中,我們的閉包是作為第二個參數傳遞給 sort.Search() 的簡單函數。
這個閉包訪問數字切片,即使它從未被傳入,并為任何大于或等于 7 的數字返回 true。通過這樣做,它允許 sort.Search() 工作而無需了解什么您使用的基礎數據類型是什么,或者您試圖滿足什么條件。它只需要知道特定索引處的值是否符合您的標準。

  • 用閉包+defer進行處理異常

package mainimport (
    "fmt")func handle() {
    defer func() {
        err := recover()
        if err != nil {
            fmt.Println("some except had happend:", err)
        }
    }()
    var a *int = nil
    *a = 100}func main() {
    handle()}

結果輸出:

some except had happend: runtime error: invalid memory address or nil pointer dereference

recover函數用于終止錯誤處理流程。一般情況下,recover應該在一個使用defer關鍵字的函數中執行以有效截取錯誤處理流程。如果沒有在發生異常的goroutine中明確調用恢復過程(調用recover函數),會導致該goroutine所屬的進程打印異常信息后直接退出
對于第三方庫的調用,在不清楚是否有panic的情況下,最好在適配層統一加上recover過程,否則會導致當前進程的異常退出,而這并不是我們所期望的。    

感謝各位的閱讀,以上就是“go語言閉包的知識點整理”的內容了,經過本文的學習后,相信大家對go語言閉包的知識點整理這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

汾西县| 望奎县| 南溪县| 当雄县| 平南县| 舒城县| 普安县| 岱山县| 清远市| 合水县| 正镶白旗| 康乐县| 高州市| 三江| 乌兰察布市| 祥云县| 玉龙| 敖汉旗| 瓦房店市| 广元市| 辰溪县| 安化县| 临沂市| 吉林省| 镇沅| 海口市| 简阳市| 扶绥县| 长丰县| 昭苏县| 揭西县| 调兵山市| 灵寿县| 永德县| 鹤壁市| 古交市| 余江县| 罗平县| 武山县| 旺苍县| 南华县|