匿名函数和闭包
匿名函数即在需要函数时定义函数,匿名函数能以变量方式传递,它常常被用于实现闭包。
匿名函数
定义格式:
func (参数列表) (返回参数列表) {
函数体
}
1. 定义并同时调用匿名函数
举例:
该例子相当于先定义了一个函数,这个函数后面直接对形参进行赋值操作,完成函数的使用。
- 需要时候定义的函数。
- 函数后面紧跟着实例化的形参变量。
package main
import "fmt"
func main() {
func(data string) {
fmt.Println("123" + data)
}("456")
}
// 123456
2. 将匿名函数赋值给变量。
将函数赋值给变量,为 函数 的实例化操作,用一个变量来代替这个函数,后面只需要在函数中传入参数即可(即: 实例化形式参数 )
package main
import "fmt"
func main() {
f1 := func(data string) {
fmt.Println("123" + data)
}
f1("abc")
}
// 123abc
闭包
闭包就是包含了自由变量的匿名函数。自由变量即使已经脱离了原有的自由变量环境也不会被删除,在闭包的作用域内可继续使用这个自由变量,同一个匿名函数和不同的引用环境组成了不同的闭包。
可对作用域内的变量的引用进行修改。
举例说明上面的说明:
package main
import "fmt"
func main() {
num := 1
fmt.Printf("%p\n", &num) // 地址
func() {
num++
fmt.Println(num) // 数值打印
fmt.Printf("%p\n", &num) // 地址
}()
func() {
num++
fmt.Println(num)
fmt.Printf("%p\n", &num)
}()
fmt.Println(num)
fmt.Printf("%p\n", &num)
}
// 0xc000014098
// 2
// 0xc000014098
// 3
// 0xc000014098
// 3
// 0xc000014098
以上程序中的匿名函数由于在函数体内部引用了外部的自由变量num而形成了闭包。闭包每次对num变量的加1操作都是对变量num引用的修改。
可以看出,物理地址没有发生变化,但是数值确发生变化,同时在第一个匿名函数中(闭包) 自变量n 发生变化,之后,n并没有重置,而是在基于保持该值,进入了第二个匿名函数中(闭包),n的数值继续发生变化,但是物理地址还是没有发生变化。
说明,在同一个作用域中(一个函数中,此处为 main(),当然可以为其他自定义的函数 ),闭包中的变量不会重置。
作用域不同的情况 — 举例:
package main
import "fmt"
func addone(i int) func() int {
return func() int {
i++
return i
}
}
func main() {
a1 := addone(0)
fmt.Println(a1())
fmt.Print("a1闭包的地址为: ")
fmt.Printf("%p\n", &a1)
fmt.Println(a1())
fmt.Print("a1闭包的地址为: ")
fmt.Printf("%p\n", &a1)
a2 := addone(10)
fmt.Println(a2())
fmt.Print("a2闭包的地址为: ")
fmt.Printf("%p\n", &a2)
}
// 1
// a1闭包的地址为: 0xc000006028
// 2
// a1闭包的地址为: 0xc000006028
// 11
// a2闭包的地址为: 0xc000006038
作用域不同,闭包定义在了自定义的addone() 函数中,因此函数会重置闭包。
在同一个addone() 中的闭包,函数的物理地址没有发生变化,输出量:闭包中的值也在发生变化
在不同的addone() 中的闭包,函数的物理地址发生变化,同时,输出量是有新的参数产生,而不是在上一个的基础上进行的改动,因此 输出量也发生变化。
每次调用闭包实例,i的值都会在原有的基础上加1。从打印的结果可以看到,两个闭包实例的地址完全不同,两个闭包的调用结果互不影响。