目录

Golang闭包的典型应用

隔离数据

使用这种方式可以隔离不想让调用者的数据,输出指定的结果;或者我们想要计算函数调用的次数。 函数计数器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 函数计数器
func counter(f func()) func() int {  
	n := 0
	return func() int {
		f()
		n += 1
		return n
	}
}

// 测试的调用函数
func foo() {
	fmt.Println("call foo")
}

func main() {
	cnt := counter(foo)
	cnt()
	cnt()
	cnt()
	fmt.Println(cnt())
}
/*
输出结果:
call foo
call foo
call foo
call foo
4
*/

counter可以接受任何输入值和返回值为空的函数,同时返回一个闭包,在这里闭包的结果是函数的调用次数。本例子中,只用cnt对闭包的引用结束后,才会销毁闭包。一个闭包只有没有外界引用时,才会连同状态一起被销毁。如果记录需要参数的函数,需要单独给counter传递需要传递的参数类型。

斐波那契数列:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func makeFibGen() func() int {
	f1 := 0
	f2 := 1
	return func() int {
		f2, f1 = f1+f2, f2
		return f1
	}
}

func main() {
	gen := makeFibGen()
	for i := 0; i < 10; i++ {
		fmt.Println(gen())
	}
}
/*
输出结果:
1
1
2
3
5
8
13
21
34
55
*/

代码理解:makeFibGen()返回的是一个闭包,这是表示斐波那契数列执行到了第几个了,闭包给的结果是第i项数列的结果。

装饰函数和创建中间件

函数是Go语言的一等公民,可以作为函数的参数进行传递。装饰器和中间件指的就是函数作为参数进行传递的情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func wrapping(f func() string) {
	fmt.Println("do my work...")
	fmt.Println("wrapping function: ", f())
	fmt.Println("my work finished !")
}

func sayHello() string {
	return "Hello !"
}

func sayByeBye() string {
	return "Bye Bye !"
}

func main() {
	wrapping(sayHello)
	wrapping(sayByeBye)
}
/*
输出:
do my work...
wrapping function:  Hello !
my work finished !
do my work...
wrapping function:  Bye Bye !
my work finished !
*/

一个函数可以根据传递进来的不同的函数产生不同的反应,传递进来的函数本质上是一个闭包。

Defer Work

这一个过程主要是与Javascript的回调函数进行对比,比如在JS中:

1
2
3
4
5
6
7
8
doWork1(a, b, function(result) {
  doWork2(result, function(result) {
    doWork3(result, function(result) {
      // use the final result here
    });
  });
});
console.log("hi!");

doWork1函数回调doWork2,而doWork2函数回调doWork3,执行顺序是doWork1->doWork2->doWork3,但是后一步的函数需要用到前一个函数的状态,根据前一个函数的处理结果进行相应的处理。

而在Go语言,直接使用闭包进行解决:

1
2
3
4
5
6
7
go func() {
  result := doWork1(a, b)
  result = doWork2(result)
  result = doWork3(result)
  // Use the final result
}()
fmt.Println("hi!")

闭包可以很好保存程序运行的状态,而且让程序的逻辑更加清晰。
参考链接:https://www.calhoun.io/5-useful-ways-to-use-closures-in-go/