匿名函數與閉包

  匿名函數是指不需要定義函數名的一種函數實現方式,它並不是一個新概念,最早可以回溯到1958年的 Lisp 語言。但是由於各種原因,C 和 C++ 一直都沒有對匿名函數給以支持,其他的各種語言,比如 JavaScript、C# 和 Objective-C 等語言都提供了匿名函數特性,當然也包含 Go 語言。

匿名函數

  在 Go 裏面,函數可以像普通變量一樣被傳遞或使用,屬於和變量一樣的一級公民,這與 C 語言的回調函數比較類似。不同的是,Go 語言支持隨時在代碼裏定義匿名函數。

  匿名函數由一個不帶函數名的函數聲明和函數體組成,如下所示:

func(a, b int, z float64) bool {
    return a*b <int(z)
}

  匿名函數可以直接賦值給一個變量或者直接執行:

f := func(x, y int) int {
    return x + y
}

func(ch chan int) {
    ch <- ACK
} (reply_chan) // 花括號後直接跟參數列表表示函數調用

閉包

  Go 的匿名函數是一個閉包,下面我們先來了解一下閉包的概念、價值和應用場景。

  • 基本概念

  閉包是可以包含自由(未綁定到特定對象)變量的代碼塊,這些變量不在這個代碼塊內或者任何全局上下文中定義,而是在定義代碼塊的環境中定義。要執行的代碼塊(由於自由變量包含在代碼塊中,所以這些自由變量以及它們引用的對象沒有被釋放)爲自由變量提供綁定的計算環境(作用域)。

  • 閉包的價值

  閉包的價值在於可以作爲函數對象或者匿名函數,對於類型系統而言,這意味着不僅要表示數據還要表示代碼。支持閉包的多數語言都將函數作爲第一級對象,就是說這些函數可以存儲到變量中作爲參數傳遞給其他函數,最重要的是能夠被函數動態創建和返回。

  • Go 語言中的閉包

  Go 語言中的閉包同樣也會引用到函數外的變量。閉包的實現確保只要閉包還被使用,那麼被閉包引用的變量會一直存在,如下面代碼所示。

package main

import (
    "fmt"
)

func main() {
    var j int = 5
    a := func()(func()) {
        var i int = 10
        return func() {
            fmt.Printf("i, j: %d, %d\n", i, j)
        }
    }()

    a()
    j *= 2
    a()
}

  上述例子的執行結果是:

i, j: 10, 5
i, j: 10, 10

  在上面的例子中,變量 a 指向的閉包函數引用了局部變量 i 和 j,i 的值被隔離,在閉包外不能被修改,改變 j 的值以後,再次調用 a,發現結果是修改過的值。

  在變量 a 指向的閉包函數中,只有內部的匿名函數才能訪問變量 i,而無法通過其他途徑訪問到,因此保證了 i 的安全性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章