🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 引入 `foo` 函数功能已经开发好了,并且运行一切正常。现在有个小需求就是要统计该函数被调用的次数。 ```go package main import fmt var count int func foo() { fmt.Println("foo函数功能") count++ } func main() { foo() foo() fmt.Printf("foo函数被调用的次数: %d", count) } // 运行结果: // foo函数功能 // foo函数功能 // foo函数被调用的次数: 2 ``` 以上的代码可以实现到需求,但是不是很完美。原因其一,添加全局变量,会带来的风险是污染全局变量。其二,改动原来正常运行的函数。 基于以上两个原因,可以改进成闭包函数。 ## 闭包函数 闭包的作用: - 可以让一个变量常驻内存 - 可以让一个变量不污染全局 闭包可以理解成 “定义在一个函数内部的函数,内部函数(匿名函数)引用非自己作用域的变量”。闭包是匿名函数的应用。 - 闭包是指有权访问另一个函数作用域中的变量的函数 - 创建闭包的常见的方式就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量 >[warning] 注意:由于闭包里作用域返回的局部变量资源不会被立刻销毁,所以可能会占用更多的内存,过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。 以下代码解决上述说到的第一个问题(污染全局变量)。 ```go package main import fmt func foo() func() int8 { var count int8 return func() int8 { fmt.Println("foo函数功能") count++ return count } } func main() { f := foo() f() count := f() fmt.Printf("foo函数被调用的次数: %d\n", count) } // 运行结果: // foo函数功能 // foo函数功能 // foo函数被调用的次数: 2 ``` >[info] 以上代码,foo函数不是闭包函数。而 `f` 函数则是闭包函数。其中 `f` 函数体在 foo 函数的匿名函数函数体。函数 `f` 调用 foo 函数的 count 变量。即是**闭包函数**。 以下代码是通过上面改良版本,解决修改原有函数体内容。该函数可以统计相同类型的函数调用次数 ```go package main import fmt func foo() { fmt.Println("foo函数功能") } func bar() { fmt.Println("bar函数功能") } func funcCounter(f func()) func() int8 { var count int8 return func() int8 { f() count++ return count } } func main() { foo := funcCounter(foo) foo() fooCount := foo() fmt.Printf("foo函数被调用的次数: %d\n", fooCount) bar := funcCounter(bar) barCount := bar() fmt.Printf("bar函数被调用的次数: %d\n", barCount) } // 运行结果: // foo函数功能 // foo函数功能 // foo函数被调用的次数: 2 // bar函数功能 // bar函数被调用的次数: 1 ``` >[info] 以上代码,main 函数的赋值 foo 、 bar 函数是闭包函数,两个函数体在 funcCounter 的匿名函数体里面。且匿名函数调用了 funcCounter 函数的 count 变量。即调用 funcCounter 函数,返回的函数即为闭包函数。 funcCounter 函数叫做装饰器函数。 由此可得,装饰器函数是闭包函数的应用。 ## 闭包相关示例 示例1 ```go /* 计数器(1) 调用一次,则累加一。 */ func counter() func() { var i int return func() { i++ fmt.Printf("i: %v\n", i) } } func main() { f1 := counter() f1() f1() f1() } // 运行结果: // i: 1 // i: 2 // i: 3 ``` 示例2 ```go /* 自定义初始化值累加器 调用一次,则累加一。 */ func counterInitValue(i int) func() int { return func() int { i++ return i } } func main() { i := 5 f2 := counterInitValue(i) i = f2() i = f2() i = f2() fmt.Printf("f2-> i: %v\n", i) } // 运行结果: // f2-> i: 8 ``` 示例3 ```go /* 文件扩展名 判断文件是否为指定扩展名。如果不是的话添加,否则直接返回文件名。 */ func fileExtension(suffix string) func(string) string { return func(name string) string { if strings.HasSuffix(name, suffix) { return name } else { return name + suffix } } } func main() { f3 := fileExtension(".json") filename1 := f3("test") filename2 := f3("config.json") fmt.Printf("filename: %s, filename: %s\n", filename1, filename2) } // 运行结果: // filename: test.json, filename: config.json ``` 示例4 ```go /* 计算器 两数相加或相减函数 */ func calc(x int, y int) (func() int, func() int) { add := func() int { return x + y } sub := func() int { return x - y } return add, sub } func main() { add, sub := calc(10, 5) fmt.Printf("add(): %v\n", add()) fmt.Printf("sub(): %v\n", sub()) } // 运行结果: // add(): 15 // sub(): 5 ```