插件窝 干货文章 Go语言闭包中,为什么循环内匿名函数总是输出循环变量的最终值?

Go语言闭包中,为什么循环内匿名函数总是输出循环变量的最终值?

变量 闭包 循环 函数 420    来源:    2025-03-14

在Go语言中,闭包(closure)是指一个函数值(function value)引用了其函数体之外的变量。闭包可以捕获和存储这些变量的引用,即使在函数执行时这些变量已经超出了其原始作用域。

当你在循环中使用匿名函数(闭包)时,如果这个匿名函数引用了循环变量,那么所有匿名函数都会共享同一个循环变量的引用。由于Go语言中的循环变量在每次迭代时都会被更新,因此所有闭包都会捕获到循环变量的最终值。

示例代码

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        go func() {
            fmt.Println(i)
        }()
    }
    time.Sleep(time.Second) // 等待goroutine执行
}

输出结果

3
3
3

原因分析

在上面的代码中,i 是循环变量,所有的匿名函数都捕获了同一个 i 的引用。当循环结束时,i 的值变为 3,此时所有的匿名函数才开始执行,因此它们都会输出 3

解决方法

为了避免这个问题,你可以在每次迭代时创建一个新的局部变量,并将循环变量的值赋给这个局部变量。这样每个闭包都会捕获到不同的变量引用。

修改后的代码

package main

import (
    "fmt"
    "time"
)

func main() {
    for i := 0; i < 3; i++ {
        i := i // 创建一个新的局部变量
        go func() {
            fmt.Println(i)
        }()
    }
    time.Sleep(time.Second) // 等待goroutine执行
}

输出结果

0
1
2

解释

在修改后的代码中,i := i 创建了一个新的局部变量 i,每个迭代中的匿名函数都捕获了这个局部变量的引用。因此,每个闭包都会输出对应的迭代值。

总结

在Go语言中,循环内的匿名函数(闭包)如果引用了循环变量,会捕获该变量的引用,而不是值。为了避免所有闭包都输出循环变量的最终值,可以在每次迭代时创建一个新的局部变量,并将循环变量的值赋给这个局部变量。这样每个闭包都会捕获到不同的变量引用,从而输出正确的值。