轮子哥的话**:闭包不是私有啊,闭的意思不是“封闭内部状态”,而是“封闭外部状态”啊。一个函数如何能封闭外部状态呢,当外部状态的scope失效的时候,还有一份留在内部状态里面……

轮子哥的话:闭包不是私有啊,闭的意思不是“封闭内部状态”,而是“封闭外部状态”啊。一个函数如何能封闭外部状态呢,当外部状态的scope失效的时候,还有一份留在内部状态里面……

ibm文档 源文档

  1. 概念:

    ​ 闭包是由函数和与其相关的引用环境组合而成的实体

  2. 函数和闭包:

    • 函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。

    • 闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

  3. 引用环境

    1. 引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。那么为什么要把引用环境与函数组合起来呢?这主要是因为在支持嵌套作用域的语言中,有时不能简单直接地确定函数的引用环境。这样的语言一般具有这样的特性:
    • 函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
    • 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。

    示例1:

    1
    2
    3
    4
    5
    6
    7
    
    eg.1
    func out() func() int{
        num:=10
        return func() int{
            return num+1
        }
    }
    

    分析:在没有闭包的情况下,当调用out()函数时,匿名函数作为返回值被返回,此时的变量num还没有传递给匿名函数,但是当匿名函数被返回后,out()函数就执行结束了,函数栈和变量都被释放了那么匿名函数就访问不到num变量了。

    示例2:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    func dot(ft func(int)) {
     for i := 0; i < 10; i++ {
         ft(i)
     }
    }
    var sum int = 10
       
    func add(i int) {
     sum = sum + i
    }
    func main() {
     dot(add)
     fmt.Println(sum)
    }
       
    

    分析:在没有闭包的情况下,dot访问不到sum变量,而add可以访问到sum变量。那么在dot函数中调用add方法,能不能访问到sum变量呢。

  4. 总结:

    1. 这样的语言中,如果按照作用域规则在执行时确定一个函数的引用环境,那么这个引用环境可能和函数定义时不同。
    2. 要想使这两段程序正常执行,一个简单的办法是在函数定义时捕获当时的引用环境,并与函数代码组合成一个整体。
    3. 当把这个整体当作函数调用时,先把其中的引用环境覆盖到当前的引用环境上,然后执行具体代码,并在调用结束后恢复原来的引用环境。这样就保证了函数定义和执行时的引用环境是相同的。
    4. 这种由引用环境与函数代码组成的实体就是闭包。