这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战
前文回顾
前面的文章主要介绍了 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
,从外部代码根本无法直接访问该变量。不同的闭包之间变量不会互相干扰,c1
和 c2
两个计数器都是独立进行计数。
小结
本文主要介绍了匿名函数和闭包。在 Go 语言中,函数可以像普通变量一样被传递或使用,这与 C 语言的回调函数比较类似。不同的是,Go 语言支持随时在代码里定义匿名函数。而 Go 的匿名函数是一个闭包。闭包可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。