Go中的闭包和defer关键字
闭包基本介绍:
闭包就是一个函数和与其相关的引用环境组合的一个整体。
package main import "fmt" func main() { //使用AddUpper函数 //当反复调用f函数,因为n是初始化一次,因此每调用一次就进行累计。 //我们要搞清楚闭包的关键,就是要分析出返回的函数引用到了哪些变量,因为函数和它引用的变量共同构成闭包。 f := AddUpper() result := f(1)//第一次n是10 那么结果就是10+1=11 fmt.Println("result", result)//11 result2 := f(2)//里面的n已经是11了 那么结果是11+2=13 fmt.Println("result2", result2)//13 } //AddUpper是一个函数返回的数据类型是:func(int) int func AddUpper() func(int) int { //闭包的说明:返回的是一个匿名函数,这个匿名函数引用到了函数外的n,因此这个匿名函数就和n就形成一个整体构成了闭包。 //可以这样理解:闭包是一个类,函数是操作,n是字段。函数和它使用到的n构成闭包 var n int = 10 return func(x int) int { n = n + x return n } }
加深对闭包理解:
package main import ( "fmt" "strconv" ) func main() { //使用AddUpper函数 f := AddUpper() result := f(1) fmt.Println("result", result) // str= hello11 result 11 result2 := f(2) //里面的n已经是11了 那么结果是11+2=13 fmt.Println("result2", result2) //str= hello1113 result2 13 } func AddUpper() func(int) int { var n int = 10 var str string = "hello" return func(x int) int { n = n + x str = str + strconv.Itoa(n) //str = hello+n fmt.Println("str=", str) return n } }
闭包的案例:
package main import ( "fmt" "strings" ) func main() { a := makeSuffix(".jpg") result3 := a("mafu") fmt.Println("result3", result3) //mafu.jpg result4 := a("haha.jpg") fmt.Println("result4", result4) //haha.jpg } //传入一个后缀,判断这个一个文件名是否包含次后缀,如果包含直接返回该文件名,不包含就加上。 //返回的匿名函数和makeSuffix(suffix string)中的suffix变量组成一个闭包,因为返回的函数引用到了suffix变量。 func makeSuffix(suffix string) func(name string) string { return func(name string) string { if !strings.HasSuffix(name, suffix) { //如果name没有指定的后缀suffix。则加上 return name + suffix } //有后缀则直接返回 return name } }
闭包的好处:如果使用传统方法,也可以完成这个需求,但是传统的方法需要每一次都传入后缀,比如.jpg。而闭包因为可以保留上次引用的某个值。所以我们传入一次就可以反复使用了。
defer关键字:
为了在函数执行完毕后,及时释放资源。Go中提供defer(延时机制)
package main import "fmt" func main() { //当sum函数执行完毕后再执行defer栈中的代码。 res := sum(1, 2) fmt.Println("res=", res) //第四 res=3 } func sum(n1 int, n2 int) int { //当执行到defer时,会将defer语句压入到独立的栈中(defer栈) defer fmt.Println("ok1 n1=", n1) //第三 ok1 n1=1 //当执行到defer时,会将defer语句压入到独立的栈中(defer栈) 栈是先进后出 所以这句语句后进先出来 defer fmt.Println("ok2 n2=", n2) //第二 ok2 n2=2 result := n1 + n2 fmt.Println("ok3 result=", result) //第一 result=3 return result }
细节说明:
-
当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数的下一条语句。
-
当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(先进后出)
-
在defer将语句放入栈时,也会将相关的值拷贝同时入栈。
func sum(n1 int, n2 int) int { //当执行到defer时,会将defer语句压入到独立的栈中(defer栈) defer fmt.Println("ok1 n1=", n1) //第三 ok1 n1=1 后面n1虽然自增+1 但是对栈中的n1没有影响 因为值拷贝 //当执行到defer时,会将defer语句压入到独立的栈中(defer栈) 栈是先进后出 所以这句语句后进先出来 defer fmt.Println("ok2 n2=", n2) //第二 ok2 n2=2 n1++ //2 n2++ //3 result := n1 + n2 fmt.Println("ok3 result=", result) //第一 result=3 return result }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)