1、Go语言支持闭包
2、Go语言能通过escape analyze识别出变量的作用域,自动将变量在堆上分配。将闭包环境变量在堆上分配是Go实现闭包的基础。
3、返回闭包时并不是单纯返回一个函数,而是返回了一个结构体,记录下函数返回地址和引用的环境中的变量地址。

用途

函数式编程,匿名函数。

示例一

  1. package main
  2. import "fmt"
  3. func adder() func(int) int {
  4. sum := 0
  5. return func(x int) int {
  6. fmt.Printf("sum addr=%p, x addr=%p\n", &sum, &x)
  7. sum += x
  8. return sum
  9. }
  10. }
  11. func main() {
  12. pos, neg := adder(), adder()
  13. fmt.Printf("pos addr=%p, neg addr=%p\n", &pos, &neg)
  14. for i := 0; i < 10; i++ {
  15. fmt.Println(
  16. pos(i),
  17. neg(-2*i),
  18. )
  19. }
  20. }

输出如下:

  1. pos addr=0xc000006028, neg addr=0xc000006030
  2. sum addr=0xc0000180d8, x addr=0xc0000180f8
  3. sum addr=0xc0000180f0, x addr=0xc000018120
  4. 0 0
  5. sum addr=0xc0000180d8, x addr=0xc000018130
  6. sum addr=0xc0000180f0, x addr=0xc000018138
  7. 1 -2
  8. sum addr=0xc0000180d8, x addr=0xc000018160
  9. sum addr=0xc0000180f0, x addr=0xc000018168
  10. 3 -6
  11. sum addr=0xc0000180d8, x addr=0xc000018190
  12. sum addr=0xc0000180f0, x addr=0xc000018198
  13. 6 -12
  14. sum addr=0xc0000180d8, x addr=0xc0000181c0
  15. sum addr=0xc0000180f0, x addr=0xc0000181c8
  16. 10 -20
  17. sum addr=0xc0000180d8, x addr=0xc0000181f0
  18. sum addr=0xc0000180f0, x addr=0xc0000181f8
  19. 15 -30
  20. sum addr=0xc0000180d8, x addr=0xc000018220
  21. sum addr=0xc0000180f0, x addr=0xc000018228
  22. 21 -42
  23. sum addr=0xc0000180d8, x addr=0xc000018250
  24. sum addr=0xc0000180f0, x addr=0xc000018258
  25. 28 -56
  26. sum addr=0xc0000180d8, x addr=0xc000018280
  27. sum addr=0xc0000180f0, x addr=0xc000018288
  28. 36 -72
  29. sum addr=0xc0000180d8, x addr=0xc0000182b0
  30. sum addr=0xc0000180f0, x addr=0xc0000182b8
  31. 45 -90

因为pos和neg都调用了adder()参数,返回了不同的闭包,所以sum在堆上分配的地址空间也不同;
所以针对pos(i)是求sum=sum+i,即0+0=0,0+1=1, 1+2=3,3+3=6…
neg是求sum=sum+(-2i),即0+(-20)=0,0+(-21)=-2,-2+(-22)=-6….

示例二

  1. package main
  2. import "fmt"
  3. func main() {
  4. var flist []func()
  5. for i := 0; i < 3; i++ {
  6. fmt.Printf("A: addr=%p, value=%d\n", &i, i)
  7. flist = append(flist, func() {
  8. fmt.Printf("B: addr=%p, value=%d\n", &i, i)
  9. })
  10. }
  11. for _, f := range flist {
  12. f()
  13. }
  14. }

输出如下:

  1. A: addr=0xc0000180d8, value=0
  2. A: addr=0xc0000180d8, value=1
  3. A: addr=0xc0000180d8, value=2
  4. B: addr=0xc0000180d8, value=3
  5. B: addr=0xc0000180d8, value=3
  6. B: addr=0xc0000180d8, value=3

这个比较好理解,三个匿名函数闭包中的i是同一个变量i,在堆上是同一个,最后i的值为3,所以三次都输出3。

示例三

  1. package main
  2. import "fmt"
  3. func main() {
  4. var flist []func()
  5. for i := 0; i < 3; i++ {
  6. //给i变量重新赋值
  7. i := i
  8. fmt.Printf("A: addr=%p, value=%d\n", &i, i)
  9. flist = append(flist, func() {
  10. fmt.Printf("B: addr=%p, value=%d\n", &i, i)
  11. })
  12. }
  13. for _, f := range flist {
  14. f()
  15. }
  16. }

输出结果如下:

  1. A: addr=0xc0000180d8, value=0
  2. A: addr=0xc0000180f8, value=1
  3. A: addr=0xc000018118, value=2
  4. B: addr=0xc0000180d8, value=0
  5. B: addr=0xc0000180f8, value=1
  6. B: addr=0xc000018118, value=2

i被重新赋值,传入闭包函数的i为新的地址,所以三个闭包函数分别为1,2,3

参考

Go语言中的闭包
Golang中闭包的实现原理