引入

有时候一段逻辑只用一次,没必要起个名字声明成独立函数。Go 允许把函数当值来用——赋给变量、当参数传递、从函数里返回,还能通过闭包”记住”外部变量。这些特性让代码更灵活,尤其在处理回调和延迟执行时。

正文

定义

定义

匿名函数是没有名字的函数,可以直接赋值给变量或立即执行。闭包是匿名函数引用了外部变量的状态,使得函数可以”记住”创建时的环境。Go 里函数是一等公民,可以作为值传递作为参数接收

语法

// 匿名函数赋值给变量
add := func(a, b int) int {
    return a + b
}
 
// 立即执行的匿名函数
result := func(a, b int) int {
    return a + b
}(3, 4)
 
// 闭包:函数引用外部变量
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
 
// 函数作为参数(回调)
func apply(fn func(int) int, x int) int {
    return fn(x)
}

例子

匿名函数赋值

greet := func(name string) {
    fmt.Println("Hello,", name)
}
 
greet("Alice")

把匿名函数赋给变量,通过变量名调用,和普通函数用起来一样。

立即执行匿名函数

result := func(a, b int) int {
    return a + b
}(3, 5)
 
fmt.Println(result) // 8

定义完直接传参执行,适合只需要运行一次的逻辑。

闭包

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
 
func main() {
    c := makeCounter()
    fmt.Println(c()) // 1
    fmt.Println(c()) // 2
    fmt.Println(c()) // 3
}

闭包捕获了外部变量 count,每次调用 c() 都修改同一个 count,函数之间共享状态。

多个闭包共享变量

func makeCounter() (func() int, func() int) {
    count := 0
    inc := func() int {
        count++
        return count
    }
    dec := func() int {
        count--
        return count
    }
    return inc, dec
}
 
func main() {
    inc, dec := makeCounter()
    fmt.Println(inc()) // 1
    fmt.Println(inc()) // 2
    fmt.Println(dec()) // 1
}

两个闭包共享同一个 count,一个负责加,一个负责减。

函数作为参数

func double(n int) int {
    return n * 2
}
 
func apply(fn func(int) int, x int) int {
    return fn(x)
}
 
func main() {
    result := apply(double, 5)
    fmt.Println(result) // 10
}

把函数当参数传进去,apply 不关心具体逻辑,只负责调用。

函数作为返回值

func multiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}
 
func main() {
    triple := multiplier(3)
    fmt.Println(triple(5)) // 15
}

multiplier 返回一个新函数,这个函数”记住”了 factor 的值。

回调模式

func fetchData(url string, onSuccess func(string), onError func(error)) {
    // 模拟请求
    data := "response data"
    if data != "" {
        onSuccess(data)
    } else {
        onError(errors.New("请求失败"))
    }
}
 
func main() {
    fetchData("https://example.com",
        func(data string) {
            fmt.Println("成功:", data)
        },
        func(err error) {
            fmt.Println("失败:", err)
        },
    )
}

回调就是把函数传给另一个函数,让它在合适的时机调用,常见于异步处理和事件驱动。

常见写法

写法说明
fn := func() {}匿名函数赋值给变量
func() {}()立即执行匿名函数
func outer() func() int { … }返回闭包
func apply(fn func(int) int, x int)函数作为参数
fn := multiplier(3)函数作为返回值
onSuccess(data)回调模式

特点

  • Go 里函数是一等公民,可以赋值、传参、返回
  • 闭包捕获外部变量的引用,不是值的副本,多次调用共享状态
  • 匿名函数常用于 defergoroutine 和回调场景
  • 闭包的变量生命周期超出外部函数,只要闭包还在用就不会被回收
  • 函数类型可以作为参数类型声明:func(int) int
  • 回调模式在 Go 里不如 JavaScript 常见,Go 更倾向用 channel 和 interface

理解

匿名函数和闭包让函数不再只是”被调用”,而是变成了可以传递和组合的数据。闭包最强大的地方是能记住外部状态,适合做计数器、工厂、延迟计算等场景。不过要注意闭包捕获的是引用,在循环里用闭包容易踩坑。

引出

函数作为参数传递已经很灵活了,Go 还支持一种特殊的参数形式——数量不定的参数。接下来看 Go 可变参数 怎么用 接收任意多个参数。