简单闭包(Closure)理解

80 阅读2分钟

闭包(Closure) 是指一个函数与其相关的引用环境(变量集合)的组合。在 Go 中,函数是一等公民,因此可以在函数内部定义函数,并且这些内部函数可以访问外部函数的变量。当一个内部函数引用了外部函数的变量时,就形成了一个闭包。

闭包在实际编程中具有很多应用,例如可以用于实现函数工厂、延迟执行、状态保持等。

下面是一个简单的闭包示例:

package main

import "fmt"

func outerFunc() func() {
	outerVar := "Hello"

	// 内部函数是一个闭包,它引用了外部函数的变量 outerVar
	innerFunc := func() {
		fmt.Println(outerVar)
	}

	return innerFunc
}

func main() {
	// 调用 outerFunc 返回内部函数 innerFunc,并将其赋值给变量 fn
	fn := outerFunc()

	// 调用 fn,它仍然可以访问 outerVar 变量
	fn() // 输出:Hello
}

在上述示例中,我们定义了一个名为 outerFunc 的函数,它内部定义了一个名为 innerFunc 的函数。innerFunc 引用了 outerFunc 的局部变量 outerVar

main 函数中,我们调用 outerFunc 并将返回的内部函数 innerFunc 赋值给变量 fn。然后,调用 fn,它仍然可以访问和打印 outerVar 的值。

通过闭包,innerFunc 可以访问 outerVar,即使 outerFunc 已经返回并且 outerVar 在其生命周期结束后仍然存在。

闭包的特性使得函数可以捕获和持有外部变量的状态,这对于某些编程模式和需求非常有用。

我们可以结合python的装饰器来理解golang闭包 下面是用python实现的一个的函数计时器装饰器

import time
def timer(func):
    def func_in():
        start_time = time.time()
        func()
        end_time = time.time()
        spend_time = (end_time - start_time)/60
        print("Spend_time:{} min".format(spend_time))
    return func_in

@timer
def test():
    # 这个函数是随便写的
    list_a = []
    for _ in range(10000000):
        list_a.append(_)
    print(len(list_a))

if __name__ == '__main__':
    test()

我们可以使用golang来实现类似的功能,不过golang没有@timer的语法,但是这里只是为了帮助理解golang闭包的概念。


func Timer(f func()) func() {
	return func() {
		startTime := time.Now()
		f()
		endTime := time.Now()
		spendTime := endTime.Sub(startTime).Minutes()
		fmt.Printf("Spend_time: %.2f min\n", spendTime)
	}
}

func main() {
	// 示例函数
	myFunc := func() {
		time.Sleep(2 * time.Second)
		fmt.Println("Hello, World!")
	}

	// 使用装饰器
	decoratedFunc := Timer(myFunc)
	decoratedFunc()
}

在上面的示例中,我们定义了一个Timer函数,它接受一个函数作为参数,并返回一个新的函数作为装饰后的函数。装饰后的函数会在调用原始函数之前和之后记录时间,并打印出运行时间。

在main函数中,我们定义了一个示例函数myFunc,它会休眠2秒并打印一条消息。然后,我们使用Timer装饰器将myFunc装饰成decoratedFunc,并调用decoratedFunc来执行装饰后的函数。运行程序后,你将看到输出的运行时间。