这是我一直试图理解的代码:
package main
import (
"fmt"
)
func squares() func() int {
var x int
return func() int {
x = x + 2
return x * x
}
}
func main() {
f := squares()
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(squares()())
fmt.Println(squares()())
fmt.Println(squares()())
}
我们得到的结果:
4
16
36
4
4
4
我的问题是:为什么 fmt.Println(squares()())
中的 x
的值保持不变?
最佳答案
精简版
每次调用 squares
时,您都在构建一个新的闭包。
这就好像您用面向对象的语言构建了一个新的 Counter 对象:
new Counter().increment(); // 4
new Counter().increment(); // 4
...相对于:
c = new Counter();
c.increment(); // 4
c.increment(); // 16
更长的版本
在您的函数中,var x int
声明了一个局部变量x
:
func squares() func() int {
var x int
return func() int {
x = x + 2
return x * x
}
}
对于任何函数,局部变量只在函数内部可见。如果您在不同的上下文中调用一个函数,则每个调用都有一组单独的内存存储,可由您的本地符号(此处为 x
)寻址。当您返回一个函数时,您的代码范围内当前可见的任何绑定(bind)都会与您的函数一起保存,这称为闭包。
闭包可以保持状态,就像对象一样。因此,您的闭包可以引用创建时可见的局部变量,即使您转义了引入局部变量的 block (谢天谢地,GC 在这里跟踪与这些变量关联的内存)。
当您定义f
时,您创建了一个新的闭包。每次调用它时,都会修改内部 x
变量引用的相同位置。但是,如果您创建新的闭包并分别调用它们一次,那么您将不会看到相同的副作用,因为每个 x
在内存中命名不同的位置。
关于Go:匿名函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37509981/