go语言中匿名函数的作用域陷阱详解

 更新时间:2022年05月06日 10:52:02   作者:Timo  
GO语言的匿名函数(anonymous function),其实就是闭包.是指不需要定义函数名的一种函数实现方式,下面这篇文章主要给大家介绍了关于go语言中匿名函数作用域陷阱的相关资料,需要的朋友可以参考下

众所周知在go语言中函数也可以当作变量在程序中使用,我们可以使用函数字面量在任何表达式内指定函数变量。但是在编写代码的时候请注意:如果一个函数在使用不是在该函数内部定义的变量时,这个变量的生命周期不是由其作用域决定的!

这段话什么意思呢,借鉴go语言圣经中的一段代码举个例子:

func square() func() int { //返回一个自己定义的函数类型
   var x int = 0
   return func() int {
      x++
      return x
   }
}
func main() {
   f := square()     
   fmt.Println(f()) // 1
   fmt.Println(f()) // 2
   fmt.Println(f()) // 3
   fmt.Println(f()) // 4
}

在这段程序中,函数square返回了一个类型为func() int的函数类型,但在每次调用f()的时候,匿名函数内部都会在x原有的值的基础上+1来更新x的值,而不是把x值赋值为0,所有这段代码最终的执行结果为1 2 3 4。

这表明里层的匿名函数能获取和更新外层的square函数的局部变量,变量x在main函数中返回square函数后依然存在。我们可以将其类比为全局变量和函数的关系来理解:把square函数看作一个独立的go程序,那么局部变量x可视为在square函数内部的全局变量,显然匿名函数每次操作的都是同一个变量。我们加上变量的地址输出验证是否正确:

func square() func() int { //返回一个自己定义的函数类型
   var x int = 0
   return func() int {
      x++
      fmt.Println(&x)
      return x
   }
}
func main() {
   f := square()
   fmt.Println(f()) 
   fmt.Println(f()) 
   fmt.Println(f()) 
   fmt.Println(f()) 
}

结果:

可见调用的变量x的确是同一个变量,那么我们在捕获迭代变量时使用匿名函数的时候就需要小心,看下面这段程序:

func square(x int) int { 
   fmt.Println(x)
   return x
}
func main() {
   var handlers []func()
   for i := 0; i < 10; i++ {
      handlers = append(handlers, func() {
         square(i)
      })
   }
   for _, handler := range handlers {
      handler()
   }
}

在这段程序中,变量i在for循环引进的一个块作用域进行声明,根据前面的经验我们可以知道:在循环里创建的所有函数变量共享相同的变量,i的值在迭代中不断更新,因此i的实际取值是循环中i的最后一个取值,并且所有的square函数都在处理同一个值,运行结果也可以证明这一点:

我们经常引入一个内部变量来解决这个问题,声明一个副本,并将这个副本的值传入而不是直接把i的值传入:

func square(x int) int {
   fmt.Println(x)
   return x * x
}
func main() {
   var handlers []func()
   for i := 0; i < 10; i++ {
      j := i
      handlers = append(handlers, func() {
         square(j)
      })
   }
   for _, handler := range handlers {
      handler()
   }
}

这样就得到了我们想要的运行结果:

总结

到此这篇关于go语言中匿名函数作用域陷阱的文章就介绍到这了,更多相关go语言匿名函数作用域内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言中Timer计时器的使用技巧详解

    Go语言中Timer计时器的使用技巧详解

    Go语言中的time包里有个Timer计时器的功能,这篇文章主要就是来和大家介绍一下Timer计时器的使用技巧,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-07-07
  • Go语言学习之链表的使用详解

    Go语言学习之链表的使用详解

    链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。本文将详细为大家介绍Go语言中链表的使用,感兴趣的可以了解一下
    2022-04-04
  • GoFrame 框架缓存查询结果的示例详解

    GoFrame 框架缓存查询结果的示例详解

    GoFrame的gdb对查询结果的缓存处理是不是非常的优雅。尤其是*gcache.Cache对象采用了适配器设计模式,可以轻松实现从单进程内存缓存切换为分布式的Redis缓存,本文重点给大家介绍GoFrame 如何优雅的缓存查询结果,感兴趣的朋友一起看看吧
    2022-06-06
  • Golang Time包与日期函数的用法详解

    Golang Time包与日期函数的用法详解

    在golang中,time包提供了时间的显示和测量用的函数,下面小编就来和大家详细聊聊Golang中Time包与日期函数的具体用法,快跟随小编一起学习一下吧
    2023-07-07
  • golang简单tls协议用法完整示例

    golang简单tls协议用法完整示例

    这篇文章主要介绍了golang简单tls用法,分析了tls协议的使用步骤及客户端与服务器端的相关实现代码,需要的朋友可以参考下
    2016-07-07
  • golang中如何使用kafka方法实例探究

    golang中如何使用kafka方法实例探究

    Kafka是一种备受欢迎的流处理平台,具备分布式、可扩展、高性能和可靠的特点,在处理Kafka数据时,有多种最佳实践可用来确保高效和可靠的处理,这篇文章将介绍golang中如何使用kafka方法
    2024-01-01
  • golang有用的库及工具 之 zap.Logger包的使用指南

    golang有用的库及工具 之 zap.Logger包的使用指南

    这篇文章主要介绍了golang有用的库及工具 之 zap.Logger包的使用指南,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 一文初探 Goroutine 与 channel基本用法

    一文初探 Goroutine 与 channel基本用法

    这篇文章主要为大家介绍了一文初探 Goroutine 与 channel基本用法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Go语言利用ssh连接服务器的方法步骤

    Go语言利用ssh连接服务器的方法步骤

    这篇文章主要介绍了Go语言利用ssh连接服务器的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Go语言多人聊天室项目实战

    Go语言多人聊天室项目实战

    这篇文章主要为大家详细介绍了Go语言多人聊天室项目实战,实现单撩或多撩等多种功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08

最新评论