函数在golang中是一等公民,闭包可以看成函数的高阶应用,是golang高级开发的必备技能。
函数
函数是一等公民(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
匿名函数
匿名函数跟普通函数是一样的,只是他没有名字。
直接使用的匿名函数:
sum := func(first int, second int) int {
return first + second
}(5, 10)
fmt.Println("result: ", sum)
将函数赋值给变量,然后调用:
sumFun := func(first int, second int) int {
return first + second
}
fmt.Println("result: ", sumFun(5, 10))
闭包
闭包是指内层函数引用了外层函数中的变量(或称为引用了自由变量)的函数,其返回值也是一个函数。闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。函数是编译期静态的概念,而闭包是运行期动态的概念。
闭包=函数+引用环境(是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合)。
引用变量
闭包对环境中变量的引用过程也可以被称为“捕获”。闭包对它作用域上部的变量可以进行修改,修改引用的变量会对变量进行实际修改。
outStr := "outer-string"
foo := func() {
outStr = "inner-string"
}
fmt.Println("before: ", outStr)
foo()
fmt.Println("after: ", outStr)
// before: outer-string
// after: inner-string
记忆效应
被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆。
func counter(initValue int) func() int {
return func() int {
initValue++
return initValue
}
}
count := counter(0)
for i:=1; i<10; i++{
fmt.Println(count())
}
// 输出:1、2...9
循环中闭包捕获外部变量的坑
闭包捕获对外部变量是通过引用的方式实现的;会随着外部变量的改变而修改。为避免此问题可:
- 通过参数方式传入外部变量;
- 定义局部变量的方式;
func delayPrint() {
// 通过参数方式保证每个变量值是不同的;
for i := 0; i < 3; i++ {
go func(i int) {
time.Sleep(time.Second * 1)
fmt.Println("By param: ", i)
}(i)
}
time.Sleep(time.Second * 4)
// 直接引用外部变量,会发现所有调用最终都捕获了同一个变量值
for i := 0; i < 3; i++ {
go func() {
time.Sleep(time.Second * 1)
fmt.Println("By clouser: ", i)
}()
}
time.Sleep(time.Second * 4)
// 通过引入局部变量方式,保证捕获的变量是不同的
for i := 0; i < 3; i++ {
tmp := i
go func() {
time.Sleep(time.Second * 1)
fmt.Println("By tmp: ", tmp)
}()
}
time.Sleep(time.Second * 4)
}
// By param: 2
// By param: 0
// By param: 1
// By clouser: 3
// By clouser: 3
// By clouser: 3
// By tmp: 0
// By tmp: 2
// By tmp: 1
转载:https://blog.csdn.net/alwaysrun/article/details/117337907
查看评论