有几个同事刚学 Go 语言,最近对匿名函数的使用有些困惑,就问了下我,我做了一些解答,后来想了下,还是可以整理记录下。
例子大致是这样:
package main
import "fmt"
func main() {
innerFun := func(i int) {
fmt.Println(i)
}
useInnerFun(innerFun)
}
func useInnerFun(f func(i int)) {
f(1)
}
当时他们认为说这个匿名函数应该在 main 函数里就已经调用了,但其实不然,在 main 函数里只是做了定义,而不是发生了调用,真正的调用是在 useInnerFun 里。
我想造成困惑的原因可能有几个点,一个是对概念的理解还不太清楚,一个是有时我们单独地去看待一个东西,没有跟别的一些东西发生联系,就容易陷入其中,那么在这里就是没拿它跟具名函数做对比。
我想或许可以通过下面几种方法来帮助他们理解这个东西。
首先我们观察定义一个具名函数和一个匿名函数时的操作过程,不管我们写哪种都好,它们其实都很像,有个 func 的关键词,然后括号用于承载参数,再用大括号包裹函数体,唯一的区别就是在外部定义的具名函数,有个具体的名字,这样我们在别的函数调用的时候,就可以指明来用。
而匿名函数只是把这个具体的名字拿出来,让函数定义本身更纯粹。
// 具名函数
func NamedFun(i int) {
fmt.Println(i)
}
func main(){
// 匿名函数的定义如下,但注意这里是会报错的,错不在于定义上有问题,而是 go 的规范,提示的错误是未被使用
func(i int) {
fmt.Println(i)
}
}
接着我们可以看下我们是如何调用一个函数的,对于具名函数,我们需要写出它的方法名,然后加上括号和实参,表示进行了调用。
func main(){
NamedFun(1)
}
// 具名函数
func NamedFun(i int) {
fmt.Println(i)
}
那么对于匿名函数,其实也一样,我们只要知道,函数名其实是一个符号,它代表的是背后的一个 func 函数。匿名函数的道理也是如此,只是它的表现形式会更为多样:
func main(){
// 匿名函数
// 1. 通过给这个匿名函数指定一个变量名 anonyFun,就相当于具名函数的方法名,同样的通过括号和实参,实现调用
anonyFun := func(i int) {
fmt.Println(i)
}
anonyFun(1)
// 2. 或者直接定义后立即使用
func(i int) {
fmt.Println(i)
}(1)
// 3. 或者作为另外一个函数的参数传入
useAnonyFun(anonyFun)
// 或
useAnonyFun(func(i int) {
fmt.Println(i)
})
}
func useAnonyFun(anony func(i int)) {
anony(1)
}
但不管怎么样,我们都能看到,如果要让一个函数发挥作用,那么就是函数/函数名+括号+实参。