函数进阶一点的使用,就是将函数当作一个变量

函数类型

若要将函数作为变量,那自然是有类型的

那函数的类型是怎样的呢?一个函数的类型就是它的签名(名称、入参列表、出参列表)

签名一样的函数,类型就一样

例如定义一个 add 函数,然后打印它的类型

1
2
3
4
5
6
7
func add(x, y int) int {
return x + y
}

func main() {
fmt.Printf("%T", add) // func(int, int) int
}

既然函数也是一种变量,那么肯定是可以相互赋值的

1
2
3
4
5
6
func main() {
fmt.Println(add(1, 2)) // 3
var f func(int, int) int
f = add
fmt.Println(f(1, 2)) // 3
}

当然了,你也可以给函数类型起别名

1
2
3
4
5
6
type MyFuncType func(int, int) int
func main() {
var f MyFuncType
f = add
fmt.Println(f(1, 2)) // 3
}

函数类型的零值是 nil ,你可以将函数变量与 nil 比较,但函数变量之间是不能比较的

函数作为参数或返回值

当你知道了函数也可以作为变量之后,你就能理解将函数作为参数或者返回值的做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func add(x, y int) int {
return x + y
}

func sub(x, y int) int {
return x - y
}

func calc(x, y int, op MyFuncType) int {
return op(x, y)
}

func main() {
println(calc(1, 2, add))
println(calc(1, 2, sub))
}

匿名函数

众所周知函数内部是不能再定义函数的,但匿名函数例外

匿名,顾名思义也就是没有名字,下面是一个例子

1
2
3
4
5
6
7
8
func main() {
f := func() {
println("Hello World")
}

f() // Hello World

}

当然,你也可以定义出来就直接运行

1
2
3
4
5
func main() {
func() {
println("Hello World")
}()
}

闭包

闭包的本质是一个函数和与其相关的引用环境组合的一个整体(实体)

闭包的引用环境包括了:闭包函数内部的非全局变量、常量、函数等

请看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func closure() func() {
var x int
return func() {
x++
println(x)
}
}

func main() {
f := closure()
f() // 1
f() // 2
f() // 3
}

closure() 中的匿名函数尝试使用 x 变量的时候,在本身找不到,就会向外找,访问到外层的变量并进行修改

这样每次调用 closure() 都会改变 x 的值,所以输出的结果是 1、2、3

延迟调用

当执行到 defer 语句时,函数和参数表达式将得到计算,但会留在本函数退出时再执行

并且遵从栈式结构顺序

1
2
3
4
5
6
7
8
9
10
func main() {

defer fmt.Println("defer 1")
defer fmt.Println("defer 2")

fmt.Println("main")
}
// main
// defer 2
// defer 1

更多的细节可以看这篇

https://blog.csdn.net/m0_46251547/article/details/123762669