开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
golang中的匿名函数与闭包用法及关系
前情提要,什么是函数?
在不同的语言中,有的叫做函数比如C系列语言。有的则叫做方法,比如java语言。不过都只是叫法不同,但功能都是相同的。在Go语言中,函数作为一等公民进行使用。那么什么是一等公民呢?这个概念过于抽象,我简而述之。
一等公民
在维基百科中一等公民有如下解释:
In programming language design, a first-class citizen (also type, object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable.
我的理解是:函数在该语言中具有和类型、对象、实例化、变量具有同等效应,同等级别的地位。大意是说,在编程语言中,所谓一等公民,是指支持所有操作的实体, 这些操作通常包括作为参数传递,从函数返回,修改并分配给变量等。如golang中int可以作为参数传递,类型赋值给变量。而golang中的函数也有如此功能,也能够作为参数进行传递,也能够进行类型赋值给变量。所以函数被称为一等公民!
什么是匿名函数?
在了解了函数之后,我们先不解释什么是匿名函数,我们先需要知道函数的用法以及常见的具名函数,看下例即可了解!
func add(i int) int {
sum := 0
for i := 1; i < 10; i++ {
sum += i
}
return sum
}
func main() {
add(1)
}
上述代码中总共有两个函数,main函数与add函数。其中main函数是一个特殊的函数体,他不能有返回值和参数,并且函数名称也必须固定,他是一个程序的入口函数。add函数则是一个普通的具名函数,可以进行参数传递和参数返回,可以对一些功能进行封装,方便调用,提高开发效率。说了这么半天,那么匿名函数到底是什么呢?
匿名函数与普通函数的区别
一般来说前端开发者对于匿名函数一定不陌生。在golang中匿名函数其实也是如此,匿名匿名,就是把名称藏起来的函数。
func(i int) int {
sum := 0
for i := 1; i < 10; i++ {
sum += i
}
return sum
}(1)
上述这个例子就是一个匿名函数。没有名称,但是可以传递参数,可以返回参数,执行和具名函数同等功能的作用,甚至于比具名函数更加简便(绝对不是因为起名困难[doge])。匿名函数的参数传递需要在行末进行形成闭包。或者不在此处传递参数,而是赋值给一个变量(因为函数是一等公民)。
a := func(i int) int {
sum := 0
for i := 1; i < 10; i++ {
sum += i
}
return sum
}
a(1)
在golang中,匿名函数通常被用于开启一个goroutine,或者是在defer语句之后被执行。
go func() {
// -- TODO
}()
==========================================================================================
defer func(){
// -- TODO
}()
什么是闭包?
闭包(Closure)是匿名函数的一个特例。当一个匿名函数所访问的变量定义在函数体的外部时,就称这样的匿名函数为闭包。听不懂吗?没关系,直接上案例一看就明白!
func sayHello() func(string) string {
t := "Hi"
c := func(b string) string {
t = t + " " + b
return t
}
return c
}
func main() {
say := sayHello()
fmt.Println(sayHello()("golang!"))
fmt.Println(say("golang!"))
}
输出结果:
Hi golang!
Hi golang!
一般闭包是在函数体内形成的。sayHello函数的返回值为一个匿名函数,并且该匿名函数中使用了sayHello函数的变量t,因此这个函数就构成了闭包。如果还是不理解就看main函数中的执行语句。sayHello函数的返回值作为一个变量赋值给了say。所以它可以使用say("golang!")
,也可以使用sayHello()("golang!")
进行调用。而这两个方式皆触发了闭包效果。即函数调用函数,在函数内部的变量可以作为调用函数的变量来使用。
闭包的练习
package main
import "fmt"
func app() func(string) string {
t := "Hi"
c := func(b string) string {
t = t + " " + b
return t
}
return c
}
func main() {
a := app()
b := app()
a("go")
fmt.Println(b("All"))
}
首先看这个例子要知道app作为一个函数,实际上是一个闭包函数。因为返回值函数内部调用了函数内部的变量,形成了闭包效果。另外,a和b虽然都是app函数,但实际上作用域并不相同。他们不享有共同空间。因此a调用和b调用的结果必然不同!
答案为:
Hi All
你是否答对了呢?
闭包的用法多种多样,此处只是作为练习列举了其中一种。如果你想深一步研究,可以自行探索!