什么是闭包
闭包在Golang中是一个非常重要的概念,它允许你在一个内部函数中访问其外部函数的变量。这些变量将在外部函数返回后继续存在。简单来说,通过闭包,你可以在一个函数中一把抓住外部函数的变量,顷刻炼化成自己的变量来使用。
闭包的语法
闭包的语法非常简单,你只需要会写匿名函数就可以了。
package main
import "fmt"
// 外部函数
func counter() func() int {
var x int // 被捕获的x
// 内部函数
return func() int {
x++ // 这里的x就是捕获的外部函数的x
return x
}
}
func main() {
// 调用外部函数,获取内部函数
getNumber := counter()
// 调用内部函数三次
fmt.Println(getNumber()) // 输出: 1
fmt.Println(getNumber()) // 输出: 2
fmt.Println(getNumber()) // 输出: 3
}
这是一个简单的示例,在这个例子中,counter是一个外部函数,它返回一个内部函数。这个内部函数引用了外部函数的变量x。每次调用返回的函数getNumber,x的值都会增加1。即使counter函数已经返回,x依然存在,因为内部函数持有对它的引用。
为什么用闭包
我可以用一个需求来回答这个问题:我需要一个可以自定义词典的用来判断一个字符串中是否有词典中的子字符串的工具。这个需求可以用闭包来实现。
package main
import (
"fmt"
"strings"
)
func HasContains(dictionary []string) func(string) bool {
return func(s string) bool {
for _, word := range dictionary {
if strings.Contains(s, word) {
return true
}
}
return false
}
}
func main() {
dictionary := []string{"hello", "world"}
has_contains := HasContains(dictionary)
fmt.Println(has_contains("nothing")) // 输出:false
fmt.Println(has_contains("hello golang")) // 输出:true
fmt.Println(has_contains("love world")) // 输出:true
}
通过闭包,我们巧妙地实现了一个可以自定义词典的子字符串检查器。通过调用HasContains函数,开发者可以根据自己的需求创建一个无需二次填参的查词器。
那么,我们来思考一下,闭包还能用来做什么呢?
或许,我们还可以用来做延时执行任务。开发中,我们经常使用defer关键字来延迟一个函数的执行。延迟执行的函数会在所属函数返回之前被调用,这在资源释放和错误处理中就帮了大忙。闭包与defer一起使用时,可以方便地在函数返回之前执行一些必要的操作。
package main
import (
"os"
)
func main() {
file, err := os.Open("testdata.dat")
if err != nil {
panic(err)
}
defer func() {
err := file.Close()
if err != nil {
panic(err)
}
}()
// 其他操作
}
在上面的示例中,我们使用os.Open函数打开一个文件,并定义一个匿名函数作为defer的参数。这个匿名函数会在所属函数(在这种情况下是main函数)返回之前被调用,无论是正常返回还是发生了错误。在匿名函数中,我们首先关闭打开的文件,如果文件关闭我们就会捕获该错误并输出。
这种结构是非常有用的,因为无论在函数中的哪个位置发生了错误,我们都可以确保文件被关闭,并且可以在延迟函数中处理其他任务或者输出我们在执行任务中捕获的错误。