匿名函数
匿名函数被称为“闭包”,是指一类无需定义的标识符(函数名)的函数或子程序。匿名函数没有函数名,只有函数体。函数可以作为一种被赋值给函数类型的变量;匿名函数往往以变量方式被传递。
匿名函数的定义
匿名函数的定义可以被理解为没有名字的函数,其定义与使用方式如下:
//定义 func (参数列表)(返回值列表){ //函数体 } //使用方式 func main() { x, y := 3, 9 defer func(a int) { fmt.Println(a, y) //y为闭包引用 }(x) x += 10 y += 100 fmt.Println(x, y) }
运行之后,输出如下:
运行上面的代码,读者肯定会发现,先打印的是最后一条输出语句,然后才打印func输出,因为defer是延迟语句。而defer设计就是为了在函数执行完毕后,及时的释放资源而设计的。(最后单独讲解)
而且上面匿名函数是一个“内联”语句。匿名函数的优越性在于,可以直接使用函数内的变量而不必声明。
匿名函数的调用
下面,我们将匿名函数赋值给变量,把函数当变量使用起来。示例如下:
func main() { x, y := 3, 9 f := func(x, y int) { fmt.Println(x + y) } f(x, y) }
回调函数
回调函数,即Callback,被主函数调用运算后会返回主函数。也就是通过函数参数传递到其他代码的某一块可执行代码的引用。
匿名函数作为回调函数使用,在Go语言中非常常见。比如,可以使用匿名函数作为参数,来实现对切片中的元素遍历操作,示例如下:
func TraversalPrint(list []int, f func(int)) { for _, value := range list { f(value) } } func main() { slice := []int{1, 2, 3, 4, 5} TraversalPrint(slice, func(value int) { fmt.Println(value) }) }
运行之后,效果如下:
defer延迟语句
在前面,我们简单的介绍了一下defer。它是为了在函数执行完毕后及时的释放资源而设计的。其具体的逻辑如下:
1.当程序执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入一个专门存储defer语句的栈中,然后继续执行函数下一个语句。
2.当函数执行完毕后,再从defer栈中依次从栈顶取出语句执行(栈的规则是先进后出,也就是最先进去的最后执行)
3.在defer将语句放入栈时,也会将相关的值复制进入栈中。所以,上面的a输出的是x的最开始的值。
示例如下:
func deferCall() { defer func1() defer func2() defer func3() } func func1() { fmt.Println("1") } func func2() { fmt.Println("2") } func func3() { fmt.Println("3") } func main() { deferCall() }
运行之后,你会发现它会输出3,2,1,这也就是栈的先进后出。
defer与return的执行顺序
假如一个函数体中,即出现了defer,也出现了return返回值,那它的先后顺序是怎样的呢?下面,我们来通过一段代码进行观察,示例如下:
var name string="liyuanjing" func func1() string { defer func() { name="fengxinyao" }() fmt.Println(name) return name } func main() { func1 :=func1() fmt.Println(name) fmt.Println(func1) }
运行之后,效果如下:
运行结果分析:
1.在主main()函数中,开始只是将函数赋值给一个变量,并没有指定函数,所以第一次输出肯定原始的name值“liyunajing”。
2.当调用函数后,在defer里改变了全局变量,此时的name的值已经变为"fengxinyao"。
3.那为什么最后输出还是“liyuanjing”呢?解释只有一个,即defer是在return后才调用的。所以在执行defer前,func1已经被赋值成了“liyuanjing”。
defer常用场景
除了释放资源之外,defer还有一个应用场景,那就是与recover()函数一起使用。
当程序出现宕机或者遇到panic错误时,recover()函数可以恢复执行,而且不会报告宕机错误。之前说过,defer不但可以在return返回前调用,也可以在程序宕机显示panic错误时,在程序出现宕机之前被执行,依次来恢复程序。