文章目录

    • 用途
    • 示例一
    • 示例二
    • 示例三
  • 参考

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

 

用途

函数式编程,匿名函数。

示例一

package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		fmt.Printf("sum addr=%p, x addr=%p\n", &sum, &x)
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	fmt.Printf("pos addr=%p, neg addr=%p\n", &pos, &neg)
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

输出如下:

pos addr=0xc000006028, neg addr=0xc000006030
sum addr=0xc0000180d8, x addr=0xc0000180f8
sum addr=0xc0000180f0, x addr=0xc000018120
0 0
sum addr=0xc0000180d8, x addr=0xc000018130
sum addr=0xc0000180f0, x addr=0xc000018138
1 -2
sum addr=0xc0000180d8, x addr=0xc000018160
sum addr=0xc0000180f0, x addr=0xc000018168
3 -6
sum addr=0xc0000180d8, x addr=0xc000018190
sum addr=0xc0000180f0, x addr=0xc000018198
6 -12
sum addr=0xc0000180d8, x addr=0xc0000181c0
sum addr=0xc0000180f0, x addr=0xc0000181c8
10 -20
sum addr=0xc0000180d8, x addr=0xc0000181f0
sum addr=0xc0000180f0, x addr=0xc0000181f8
15 -30
sum addr=0xc0000180d8, x addr=0xc000018220
sum addr=0xc0000180f0, x addr=0xc000018228
21 -42
sum addr=0xc0000180d8, x addr=0xc000018250
sum addr=0xc0000180f0, x addr=0xc000018258
28 -56
sum addr=0xc0000180d8, x addr=0xc000018280
sum addr=0xc0000180f0, x addr=0xc000018288
36 -72
sum addr=0xc0000180d8, x addr=0xc0000182b0
sum addr=0xc0000180f0, x addr=0xc0000182b8
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…

 

示例二

package main

import "fmt"

func main() {
	var flist []func()
	for i := 0; i < 3; i++ {
		fmt.Printf("A: addr=%p, value=%d\n", &i, i)
		flist = append(flist, func() {
			fmt.Printf("B: addr=%p, value=%d\n", &i, i)
		})
	}

	for _, f := range flist {
		f()
	}
}

输出如下:

A: addr=0xc0000180d8, value=0
A: addr=0xc0000180d8, value=1
A: addr=0xc0000180d8, value=2
B: addr=0xc0000180d8, value=3
B: addr=0xc0000180d8, value=3
B: addr=0xc0000180d8, value=3

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

 

示例三

package main

import "fmt"

func main() {
	var flist []func()
	for i := 0; i < 3; i++ {
		//给i变量重新赋值
		i := i
		fmt.Printf("A: addr=%p, value=%d\n", &i, i)
		flist = append(flist, func() {
			fmt.Printf("B: addr=%p, value=%d\n", &i, i)
		})
	}
	for _, f := range flist {
		f()
	}
}

输出结果如下:

A: addr=0xc0000180d8, value=0
A: addr=0xc0000180f8, value=1
A: addr=0xc000018118, value=2
B: addr=0xc0000180d8, value=0
B: addr=0xc0000180f8, value=1
B: addr=0xc000018118, value=2

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

参考

Go语言中的闭包

Golang中闭包的实现原理