在学Golang之前,我一直写的是Java,而在Java中,闭包我几乎没用过,虽然好像Java也有闭包。对于我这样的一个前Java程序员来说,理解闭包有点困难,因此我换一种思路,企图用面向对象的思维去理解闭包,在JS中,闭包甚至可以实现面向对象。当然这种理解方式可能有失偏颇,属于我的个人理解,仅供参考。本文将以Golang官网的斐波那契示例为例,足够经典,也容易理解。

什么是闭包

这是我们首先提出的一个问题,那么什么是闭包,这是摘自维基百科的定义:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定(如C++)。

看了一头雾水是不是,我也一头雾水,下面再来看看golang官方对闭包的定义:

A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.

讲得还算清晰,但是也是有点不太懂。

我对闭包的理解:函数里面有函数,就这么简单

如何理解闭包

下面用Golang的官方示例来理解一下,闭包是个啥

package main

import "fmt"

// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    f := fib()
    // Function calls are evaluated left-to-right.
    fmt.Println(f(), f(), f(), f(), f())
}

正如示例里面看到的那样,fib()函数里面,又包含了一个匿名函数,这样的结构就可以称之为闭包。

我在起初看这个示例的时候,有点懵,不懂为什么每次调用函数的时候a,b的值会改变,而不是从0 开始

换一种方式,可以这么理解,因为 f()的生命周期还没有结束,所以 a,b的值仍然在内存中,这个 f()函数可以理解为一个对象。

换成面向对象的写法,就很容易理解了:

package main

type Fib struct {
    a int
    b int
}

func (f *Fib) Fib() int {
    f.a, f.b = f.b, f.a+f.b
    return f.a
}

func main() {
    f := Fib{0, 1}
    println(f.Fib(), f.Fib(), f.Fib(), f.Fib(), f.Fib())
}

在这两个示例中,函数 f()的内部变量 a,b,相当于下面对象 f的成员变量 a,b;函数 f()内部的匿名函数,相当于对象 f的方法 fib();而 f := fib()这个操作,则相当于 f := Fib{0, 1},新建了一个对象,如此去理解,就能很好的理解闭包这个概念了。

所以闭包有什么用

按照某些说法,闭包的最初目的是为了减少全局变量,在函数调用过程中隐式地传递共享变量。然而这种操作的结果不够直接,所以一般不建议使用闭包。

最后提一嘴

这篇文章是我在理解闭包的时候有感而发的一篇文章,文中很多东西可能不是特别正确,甚至有失偏颇,本文仅供参考。

最后修改:2021 年 09 月 13 日
如果觉得我的文章对你有用,请随意赞赏