Skip to content

closure.go

文件信息

  • 📄 原文件:04_closure.go
  • 🔤 语言:go

完整代码

go
package main

import "fmt"

// ============================================================
//                      匿名函数与闭包
// ============================================================
// 匿名函数:没有名字的函数,可以在需要时定义
// 闭包:可以访问其外部作用域变量的函数
//
// 【核心概念】闭包 = 函数 + 引用环境
// 闭包会"捕获"外部变量,即使外部函数已返回

func main04() {
	fmt.Println("\n==================== 04_closure ====================")
	fmt.Println("=== 匿名函数 ===")

	// ----------------------------------------------------------
	// 定义并立即调用
	// ----------------------------------------------------------
	// 语法: func(参数) 返回类型 { 函数体 }(实参)
	result := func(a, b int) int {
		return a + b
	}(3, 4) // 立即调用

	fmt.Println("立即调用匿名函数: 3 + 4 =", result)

	// ----------------------------------------------------------
	// 赋值给变量
	// ----------------------------------------------------------
	// 匿名函数可以赋值给变量,之后通过变量调用
	multiply := func(a, b int) int {
		return a * b
	}

	fmt.Println("multiply(5, 6) =", multiply(5, 6))
	fmt.Println("multiply(7, 8) =", multiply(7, 8))

	// 可以重新赋值
	multiply = func(a, b int) int {
		return a * b * 2 // 不同的实现
	}
	fmt.Println("新 multiply(5, 6) =", multiply(5, 6))

	// ----------------------------------------------------------
	// 闭包:捕获外部变量
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包基础 ===")

	// 外部变量
	counter := 0

	// 闭包捕获 counter
	increment := func() {
		counter++ // 访问并修改外部变量
		fmt.Println("counter =", counter)
	}

	increment() // counter = 1
	increment() // counter = 2
	increment() // counter = 3

	fmt.Println("外部 counter =", counter) // 3

	// ----------------------------------------------------------
	// 闭包工厂:返回闭包
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包工厂 ===")

	// 每次调用 makeCounter 都创建新的计数器
	counter1 := makeCounter()
	counter2 := makeCounter()

	fmt.Println("counter1:", counter1()) // 1
	fmt.Println("counter1:", counter1()) // 2
	fmt.Println("counter1:", counter1()) // 3

	fmt.Println("counter2:", counter2()) // 1(独立的计数器)
	fmt.Println("counter2:", counter2()) // 2

	fmt.Println("counter1:", counter1()) // 4(继续自己的计数)

	// ----------------------------------------------------------
	// 带参数的闭包工厂
	// ----------------------------------------------------------
	fmt.Println("\n=== 带参数的闭包工厂 ===")

	addFive := makeAdder(5)
	addTen := makeAdder(10)

	fmt.Println("addFive(3) =", addFive(3))   // 8
	fmt.Println("addTen(3) =", addTen(3))     // 13
	fmt.Println("addFive(10) =", addFive(10)) // 15

	// ----------------------------------------------------------
	// 闭包陷阱:循环变量
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包陷阱:循环变量 ===")

	// 【错误示例】
	fmt.Println("错误方式:")
	var funcsWrong []func()
	for i := 0; i < 3; i++ {
		funcsWrong = append(funcsWrong, func() {
			fmt.Println("  i =", i) // 捕获的是同一个 i
		})
	}
	for _, f := range funcsWrong {
		f() // 全部输出 3!
	}

	// 【正确示例1】使用参数传递
	fmt.Println("正确方式1(参数传递):")
	var funcsRight1 []func()
	for i := 0; i < 3; i++ {
		funcsRight1 = append(funcsRight1, func(n int) func() {
			return func() {
				fmt.Println("  n =", n)
			}
		}(i)) // 立即传递当前值
	}
	for _, f := range funcsRight1 {
		f()
	}

	// 【正确示例2】创建局部变量(Go 1.22+ 循环变量已改进)
	fmt.Println("正确方式2(局部变量):")
	var funcsRight2 []func()
	for i := 0; i < 3; i++ {
		i := i // 创建新的局部变量
		funcsRight2 = append(funcsRight2, func() {
			fmt.Println("  i =", i)
		})
	}
	for _, f := range funcsRight2 {
		f()
	}

	// ----------------------------------------------------------
	// 闭包用途:延迟执行
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包用途:defer ===")

	demoDefer()

	// ----------------------------------------------------------
	// 闭包用途:回调函数
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包用途:回调 ===")

	numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	// 使用闭包作为过滤条件
	evens := filter(numbers, func(n int) bool {
		return n%2 == 0
	})
	fmt.Println("偶数:", evens)

	// 使用闭包作为映射函数
	doubled := mapSlice(numbers, func(n int) int {
		return n * 2
	})
	fmt.Println("翻倍:", doubled)

	// ----------------------------------------------------------
	// 闭包用途:状态封装
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包用途:状态封装 ===")

	// 类似于面向对象的私有状态
	acc := makeAccumulator(100)
	fmt.Println("初始余额: 100")
	fmt.Println("存款 50:", acc(50))
	fmt.Println("取款 -30:", acc(-30))
	fmt.Println("存款 20:", acc(20))

	// ----------------------------------------------------------
	// 闭包 vs 结构体方法
	// ----------------------------------------------------------
	fmt.Println("\n=== 闭包 vs 结构体 ===")

	// 闭包方式
	fib := makeFibonacci()
	fmt.Print("斐波那契(闭包): ")
	for i := 0; i < 10; i++ {
		fmt.Print(fib(), " ")
	}
	fmt.Println()

	// 结构体方式(见 05_methods.go)
}

// ============================================================
//                      闭包函数定义
// ============================================================

// ----------------------------------------------------------
// 闭包工厂:计数器
// ----------------------------------------------------------
// 每次调用返回一个新的计数器函数
// 每个计数器有自己独立的 count 变量
func makeCounter() func() int {
	count := 0 // 这个变量被闭包捕获
	return func() int {
		count++
		return count
	}
}

// ----------------------------------------------------------
// 闭包工厂:加法器
// ----------------------------------------------------------
// 返回一个"加 n"的函数
func makeAdder(n int) func(int) int {
	// n 被闭包捕获
	return func(x int) int {
		return x + n
	}
}

// ----------------------------------------------------------
// 闭包工厂:累加器
// ----------------------------------------------------------
func makeAccumulator(initial int) func(int) int {
	balance := initial
	return func(amount int) int {
		balance += amount
		return balance
	}
}

// ----------------------------------------------------------
// 闭包工厂:斐波那契生成器
// ----------------------------------------------------------
func makeFibonacci() func() int {
	a, b := 0, 1
	return func() int {
		result := a
		a, b = b, a+b
		return result
	}
}

// ----------------------------------------------------------
// defer 中的闭包
// ----------------------------------------------------------
func demoDefer() {
	x := 10

	// 方式1: 直接传值(值在 defer 时确定)
	defer fmt.Printf("  defer 传值: x = %d\n", x)

	// 方式2: 闭包捕获(值在执行时确定)
	defer func() {
		fmt.Printf("  defer 闭包: x = %d\n", x)
	}()

	x = 20
	fmt.Println("  函数中: x =", x)
}

// ----------------------------------------------------------
// 高阶函数:过滤
// ----------------------------------------------------------
func filter(nums []int, predicate func(int) bool) []int {
	result := []int{}
	for _, n := range nums {
		if predicate(n) {
			result = append(result, n)
		}
	}
	return result
}

// ----------------------------------------------------------
// 高阶函数:映射
// ----------------------------------------------------------
func mapSlice(nums []int, mapper func(int) int) []int {
	result := make([]int, len(nums))
	for i, n := range nums {
		result[i] = mapper(n)
	}
	return result
}

// ============================================================
//                      重要注意事项
// ============================================================
//
// 1. 【闭包捕获变量的引用,不是值】
//    外部变量改变,闭包看到的值也变
//    闭包修改变量,外部也能看到
//
// 2. 【循环变量陷阱】
//    Go 1.22 之前,循环变量是共享的
//    Go 1.22+,每次迭代创建新变量
//    建议:总是显式创建局部变量
//
// 3. 【内存考虑】
//    闭包会持有外部变量的引用
//    可能导致变量无法被垃圾回收
//    长期运行的闭包要注意内存泄漏
//
// 4. 【闭包 vs 方法】
//    闭包:适合简单状态、临时使用
//    方法:适合复杂状态、需要多个操作
//
// 5. 【常见用途】
//    - 延迟计算
//    - 回调函数
//    - 状态封装
//    - 工厂函数
//    - 装饰器模式
//
// 6. 【调试困难】
//    闭包没有名字,调试时栈信息不清晰
//    复杂场景考虑使用命名函数或方法

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布