匿名函数&闭包(golang)

1.go匿名函数

1.1 基本概念

匿名函数是指没有函数名的函数定义,可以直接定义并使用。在Go中,函数是一等公民,可以像普通变量一样被赋值和传递。

1.2 基本语法

func(参数列表) 返回值类型 {
    // 函数体
}

1.3 使用方式

1.3.1 直接调用

package main

import "fmt"

func main() {
	result := func(a, b int) int {
		return a + b
	}(3, 6)
	fmt.Println(result)
}

输出:
在这里插入图片描述

1.3.2 赋值给变量

package main

import "fmt"

func main() {
	add := func(a, b int) int {
		return a + b
	}
	fmt.Println(add(4, 9))
}

输出:
在这里插入图片描述

1.3.3 作为参数传递

package main

import "fmt"

func calculate(a, b int, op func(int, int) int) int {
	return op(a, b)
}

func main() {
	sum := calculate(3, 4, func(x, y int) int {
		return x + y
	})

	fmt.Println(sum)
}

输出:
在这里插入图片描述

2.闭包

2.1 基本概念

闭包是匿名函数的一个特殊形式,它可以捕获并持有其外部作用域中的变量。即使外部函数已经执行完毕,闭包仍然可以访问这些变量。

2.2 闭包的特性

2.2.1 捕获外部变量(闭包可以访问定义它的函数中的变量)

package main

import "fmt"

func main() {
	x := 10

	closure := func() {
		fmt.Println(x) // 可以访问外部变量x
		x++            // 也可以修改它
	}

	closure() // 输出: 10     
	fmt.Println(x)  // 输出: 11 (x被修改了) 
}

2.2.2 保持状态(闭包会"记住"这些变量的值)

package main

import "fmt"

func main() {
	nextInt := intSeq()

	fmt.Println(nextInt())  // 1
	fmt.Println(nextInt())  // 2
	fmt.Println(nextInt())  // 3
}

func intSeq() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

2.2.3 独立状态(每次创建闭包都会创建新的状态)

package main

import "fmt"

func intSeq() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

func main() {
	a := intSeq()
	b := intSeq()

	fmt.Println(a()) // 1
	fmt.Println(a()) // 2
	fmt.Println(b()) // 1 (新的闭包,新的状态)
	fmt.Println(a()) // 3
}

2.3 基本示例

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter1 := makeCounter()
    fmt.Println(counter1()) // 1
    fmt.Println(counter1()) // 2
    
    counter2 := makeCounter()
    fmt.Println(counter2()) // 1 (新的闭包,新的状态)
    fmt.Println(counter1()) // 3 (原来的闭包继续维持状态)
}

2.4 闭包的常见用途

2.4.1 实现生成器

package main

import "fmt"

func idGenerator() func() int {
	id := 0
	return func() int {
		id++
		return id
	}
}

func main() {
	gen := idGenerator()
	fmt.Println(gen()) // 1
	fmt.Println(gen()) // 2
}

2.4.2 函数工厂

package main

import "fmt"

func makeAdder(x int) func(int) int {
	return func(y int) int {
		return x + y
	}
}

func main() {
	add := makeAdder(5)
	fmt.Println(add(3)) // 8
}

2.4.3 延迟执行

package main

import "fmt"

func main() {
	x := 10
	defer func() {
		fmt.Println("延迟执行时x的值:", x)
	}()
	x = 20
}

输出:
在这里插入图片描述
原因分析:
1.defer 捕获的是变量,不是值

  • 当使用 defer 调用一个匿名函数时,该函数会形成一个闭包,捕获的是变量 x 的引用,而不是 x 在 defer 语句执行时的值(即
    10)。
  • 因此,x 后续被修改为 20 时,defer 函数内部访问的是最终的 x 值。

2.执行顺序

  • x := 10 → x 初始化为 10
  • defer 注册匿名函数,但不立即执行(此时 x 是 10,但 defer 捕获的是 x 本身)
  • x = 20 → x 被修改为 20
  • main() 函数结束,defer 函数执行,此时读取 x 的值是 20

2.4.4 在并发编程中使用

package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 0; i < 5; i++ {
		go func(n int) {
			fmt.Println(n)
		}(i) // 注意这里传递i作为参数
	}
	time.Sleep(time.Second)
}

输出:
在这里插入图片描述

3.匿名函数与闭包的区别

特性匿名函数闭包
定义没有函数名的函数能捕获外部变量的匿名函数
变量访问只能访问自己的局部变量可以访问外部函数的变量
状态保持无状态可以保持状态
内存占用较小可能较大(因为要保存捕获的变量)
典型用途简单的一次性操作需要保持状态的场景

4.注意事项

4.1 循环中的闭包陷阱

package main

import (
	"fmt"
)

func main() {
	var funcs []func()
	for i := 0; i < 3; i++ {
		funcs = append(funcs, func() {
			fmt.Println(i) // 全部输出3
		})
	}
	for _, f := range funcs {
		f()
	}

	// 正确写法
	var funcs2 []func()
	for i := 0; i < 3; i++ {
		j := i // 创建局部变量副本
		funcs2 = append(funcs2, func() {
			fmt.Println(j) // 输出0,1,2
		})
	}
	for _, f := range funcs2 {
		f()
	}
}

4.2 内存泄露风险

package main

func main() {
	bigData := make([]byte, 1<<20) // 1MB数据

	// 即使bigData不再使用,闭包仍持有引用
	closure := func() {
		_ = bigData[0]
	}

	// closure仍然持有bigData的引用,无法被GC回收
	_ = closure
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值