在 Go 语言中,闭包(Closure)是一个非常重要的概念。它允许函数捕获并引用其外部作用域中的变量,即使这个外部作用域已经结束,被捕获的变量仍然可以被访问和修改。
1. 闭包
当一个匿名函数引用了其外层作用域中的变量时,这个组合就构成了闭包。这种机制让开发者可以在不改变全局命名空间的情况下共享数据,并且有助于构建更加模块化和易于维护的应用程序。
特性:
- 闭包可以访问定义它的外部函数中的局部变量。
- 即使外部函数已经返回,闭包仍然可以访问和修改这些变量。
- 闭包在函数返回后仍然保持对外部变量的引用。
2. 闭包的基本形式
在 Go 中,闭包通常以匿名函数的形式出现,并且它可以捕获外部作用域中的变量。
如:
func a() func() int {
i := 0
b := func() int {
i++
fmt.Println(i)
return i
}
return b
}
func main() {
c := a()
c() // 1
c() // 2
c() // 3
a() // 没有信息输出
}
- a函数定义了一个局部变量 i,并返回一个匿名函数 b 。
- 这个匿名函数形成了一个闭包,因为它捕获了外部变量 i 。
- 每次调用 c() 时,闭包会递增并返回 i 的值。
- 即使 a 函数已经返回,闭包仍然可以访问和修改 i 。
直接调用 a()
不会输出任何信息,因为 a 函数本身并没有直接执行任何输出操作,仅仅是定义并返回一个嵌套函数 b
的引用。
3. 闭包的工作原理
(1) 变量捕获
当一个匿名函数引用了外部作用域中的变量时,Go 会确保这些变量在匿名函数返回后仍然存在。这种机制被称为“变量捕获”。
(2) 值捕获 vs 引用捕获
- 在 Go 中,闭包捕获的是变量的引用,而不是变量的值。这意味着,闭包访问的是变量的当前值,而不是它在闭包创建时的值。
- 这意味着多个闭包可以共享同一个外部变量的状态。
// 返回2个函数类型的返回值
func test01(base int) (func(int) int, func(int) int) {
// 定义2个函数,并返回
// 相加
add := func(i int) int {
base += i
return base
}
// 相减
sub := func(i int) int {
base -= i
return base
}
// 返回
return add, sub
}
func main() {
f1, f2 := test01(10)
// base一直是没有消
fmt.Println(f1(1), f2(2))
// 此时base是9
fmt.Println(f1(3), f2(4))
}
由此,我们可以解释闭包在稍后的时间点被调用时,产生的“延迟引用”现象,即闭包总是引用变量的当前值。
看下面这个例子:
function createFunctions() {
var result = [];
for (var i = 0; i < 3; i++) {
result.push(function () {
return i;
});
}
return result;
}
var funcs = createFunctions();
console.log(funcs[0]()); // 输出 3
console.log(funcs[1]()); // 输出 3
console.log(funcs[2]()); // 输出 3
- 在
createFunctions
函数中,循环体内的匿名函数被添加到数组result
中。 - 这些匿名函数形成了闭包,它们捕获了变量
i
的引用。 - 当循环结束时,变量
i
的最终值是3
(因为循环结束后i
的值为3
)。 - 因此,无论调用哪个匿名函数(
funcs[0]()
、funcs[1]()
或funcs[2]()
),它们都会返回i
的当前值,即3
。
由于闭包捕获的是变量的引用,而不是值,因此当闭包在稍后的时间点被调用时,它会访问变量的最新值,而不是它在闭包创建时的值。这就是所谓的“延迟引用现象”。
再看这个例子:
function createFunction() {
var x = 10;
var innerFunc = function () {
console.log(x);
};
x = 20; // 修改变量 x 的值
return innerFunc;
}
var func = createFunction();
func(); // 输出 20
- 在
createFunction
中,闭包innerFunc
捕获了变量x
的引用。 - 在闭包创建之后,变量
x
的值被修改为20
。 - 当调用
func()
时,闭包访问的是变量x
的当前值,即20
,而不是它在闭包创建时的值10。
以上就是我对闭包的理解
~