golang基础(26.匿名函数与闭包)
匿名函数
匿名函数是一种不需要定义函数名称的声明方式,Go 语言中也提供了对匿名函数的支持,并且形式上和 PHP 类似,无非是要声明参数类型和返回值类型而已:
package main
import "fmt"
func main() {
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2)) // 赋值变量调用匿名函数 add
c := func(a, b int) int {
return a + b
}(1, 2)
fmt.Println(c) // 直接调用
}
闭包
Go 语言的匿名函数是一个闭包(Closure),下面我们先来了解一下闭包的概念、价值和应用场景。
闭包的概念和价值
所谓闭包是指引用了自由变量的函数,被引用的自由变量将和这个函数一同存在,即使离开了创造它的上下文环境也不会被释放掉。或者通俗点说,「闭」的意思是「封闭外部状态」,即使外部状态已经失效,闭包内部依然保留了一份从外部引用的变量。闭包的价值在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一类对象(firt-class object,有的地方也译作第一级对象,第一类公民等),就是说这些函数可以存储到变量中作为参数传递给其他函数,能够被函数动态创建和返回。 :::success 注:所谓第一类对象指的是运行期可以被创建并作为参数传递给其他函数或赋值给变量的实体,在绝大多数语言中,数值和基本类型都是第一类对象,在支持闭包的编程语言中(比如 Go、PHP、JavaScript、Python 等),函数也是第一类对象,而像 C、C++ 等不支持闭包的语言中,函数不能在运行期创建,所以在这些语言中,函数不是不是第一类对象。 :::
Go 语言中闭包的应用场景
Go 语言中的闭包同样也会引用函数外定义的变量,只要闭包还在被使用,那么被闭包引用的变量会一直存在。保证局部变量的安全性闭包内部声明的局部变量无法从外部修改,从而确保了安全性(类似类的私有属性):
package main
import "fmt"
func main() {
var j = 1
f := func() {
var i int
fmt.Printf("i, j: %d, %d
", i, j)
}
f()
j += 2
f()
}
上述代码输出结果如下:
i, j: 1, 1
i, j: 1, 3
在上面的示例中,变量 f 指向的闭包引用了局部变量 i 和 j,i 在闭包内部定义,其值被隔离,不能从外部修改,而变量 j 在闭包外部定义,所以可以从外部修改,闭包持有的只是其引用。
将匿名函数作为参数
我们可以先声明一个外部函数的参数为函数类型,然后定义一个闭包并赋值给指定变量,再将这个变量传递到外部函数中:
package main
import "fmt"
func callBack(f func()) {
f()
}
func main() {
var j = 1
f := func() {
var i int
j += 1
fmt.Printf("i, j: %d, %d
", i, j)
}
f()
j += 2
f()
callBack(f)
fmt.Println(j)
}
将匿名函数作为返回值
最后,我们来演示下闭包作为函数返回值的示例:
package main
import "fmt"
func addFunc(a int) func(b int) int {
return func(b int) int {
b++
return b
}
}
func main() {
f := addFunc(1)
fmt.Println(f(1))
}
我们声明 addfunc 函数的返回值是一个函数,并且在 return 时返回一个闭包,这个闭包可以访问外部函数的参数和变量,并持有这些变量,只要闭包存在,这些变量就一直存在,即使脱离了 addfunc 函数的作用域,依然可以访问它们。