夯實基礎:Go 語言匿名函數和閉包的應用實踐

前文回顧

前面的文章主要介紹了 Go 語言中函數聲明和參數傳遞。本文將會繼續介紹匿名函數和閉包相關的概念以及使用方法。

匿名函數沒有函數名,只有函數體,它只有在被調用的時候纔會被初始化。匿名函數一般被當作一種類型被賦值給類型爲函數類型的變量,經常用於實現回調函數和閉包等功能。

匿名函數和閉包

Golang 的匿名函數的聲明樣式如下所示:

func(params)(return params){
 function body
}

匿名函數的聲明與普通函數的定義基本一致,除了沒有名字之外。我們可以在匿名函數聲明之後直接調用它,如下例子所示:

func (name string){
 fmt.Println("My name is ", name)
}("王小二")

在聲明匿名函數之後,在其後加上調用的參數列表,即可立即對匿名函數進行調用。除此之外,我們還可以將匿名函數賦值給函數類型的變量,用於多次調用或者求值,如下例子所示:

currentTime := func() {
 fmt.Println(time.Now())
}
// 調用匿名函數
currentTime()

上述例子中,我們通過匿名函數實現了一個簡單的報時器,並賦值給 currentTime,每次調用 currentTime,我們都能知道當前系統的最新時間。

匿名函數一個比較常用的場景是用作回調函數。我們在接下來的例子中定義這樣一個函數:它接受 string 和匿名函數的參數輸入,然後使用匿名函數對 string 進行處理,代碼如下所示:

package main

import "fmt"

func proc(input string, processor func(str string))  {
 // 調用匿名函數
 processor(input)
}

func main()  {
 proc("王小二", func(str string) {
  for _, v := range str{
   fmt.Printf("%c\n", v)
  }
 })
}

上面代碼中的匿名函數被作爲回調函數用於對傳遞的字符串進行處理,用戶可以根據自己的需要傳遞不同的匿名函數實現對字符串進行不同的處理操作。

閉包是攜帶狀態的函數,它是將函數內部和函數外部連接起來的橋樑。通過閉包,我們可以讀取函數內部的變量。我們也可以使用閉包封裝私有狀態,讓它們常駐於內存當中。

閉包能夠引用其作用域上部的變量並進行修改,被捕獲到閉包中的變量將隨着閉包的生命週期一直存在,函數本身是不存儲信息的,但是閉包中的變量使閉包本身具備了存儲信息的能力。我們可以用閉包的特性實現一個簡單的計數器,代碼如下所示:

package main

import "fmt"

func createCounter(initial int) func() int {

 if initial < 0{
  initial = 0
 }

 // 引用 initial,創建一個閉包
 return func() int{
  initial++
  // 返回當前計數
  return initial;
 }

}

func main()  {

 // 計數器 1
 c1 := createCounter(1)

 fmt.Println(c1()) // 2
 fmt.Println(c1()) // 3

 // 計數器 2
 c2 := createCounter(10)

 fmt.Println(c2()) // 11
 fmt.Println(c1()) // 4

}

createCounter 函數返回了一個閉包,該閉包中封裝了計數值 initial,從外部代碼根本無法直接訪問該變量。不同的閉包之間變量不會互相干擾,c1c2 兩個計數器都是獨立進行計數。

小結

本文主要介紹了匿名函數和閉包。在 Go 語言中,函數可以像普通變量一樣被傳遞或使用,這與 C 語言的回調函數比較類似。不同的是,Go 語言支持隨時在代碼裏定義匿名函數。而 Go 的匿名函數是一個閉包。閉包可以作爲函數對象或者匿名函數,對於類型系統而言,這意味着不僅要表示數據還要表示代碼。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/oUDrcGNHIHnoQl9LAAAU_w