6.6 闭包

本篇学习 Go 闭包的基本原理,并通过逐行解释理解闭包函数结构与执行流程。

字数 966 字

闭包

概念说明

闭包可以理解为“函数 + 它引用的外部变量”。
外层函数执行结束后,内层函数仍然可以继续使用这些被捕获的变量。
它适合实现延迟执行、计数器、配置函数等场景。

语法/规则

  1. 闭包本质是返回一个匿名函数,且匿名函数引用了外层变量。
  2. 被捕获的变量会在后续调用中持续生效。
  3. 闭包逻辑应尽量保持单一职责,避免过度嵌套影响可读性。

延时求和示例

目标:先传入延时参数,再传入多个整数并求和。
使用方式:awaitAdd(2)(1, 2, 3)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
	"fmt"
	"time"
)

func awaitAdd(delaySecond int) func(...int) int {
	return func(numList ...int) int {
		time.Sleep(time.Duration(delaySecond) * time.Second)

		sum := 0
		for _, num := range numList {
			sum += num
		}
		return sum
	}
}

func main() {
	fmt.Println(awaitAdd(2)(1, 2, 3))
}

逐行解释(核心函数)

先只看核心这段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func awaitAdd(delaySecond int) func(...int) int {
	return func(numList ...int) int {
		time.Sleep(time.Duration(delaySecond) * time.Second)

		sum := 0
		for _, num := range numList {
			sum += num
		}
		return sum
	}
}

逐行解释如下:

  1. func awaitAdd(delaySecond int) func(...int) int {
    定义外层函数 awaitAdd
    它接收一个参数 delaySecond(延时秒数),返回值不是普通值,而是“另一个函数类型” func(...int) int

  2. return func(numList ...int) int {
    返回一个匿名函数。这个匿名函数接收多个整数(可变参数),并返回一个整数(求和结果)。

  3. time.Sleep(time.Duration(delaySecond) * time.Second)
    先暂停 delaySecond 秒。这里用了外层函数的变量 delaySecond,这就是闭包捕获外部变量。

  4. sum := 0
    定义累加器,初始值为 0。

  5. for _, num := range numList {
    遍历传进来的所有整数参数。

  6. sum += num
    把每个数字累加到 sum

  7. return sum
    把累加结果返回给调用方。

  8. 最后的 } } }
    依次结束:匿名函数体、return func 语句所在块、外层函数体。

逐行解释(调用代码)

1
2
3
func main() {
	fmt.Println(awaitAdd(2)(1, 2, 3))
}
  1. awaitAdd(2):先调用外层函数,得到一个“已经记住延时 2 秒”的新函数。
  2. (1, 2, 3):再调用这个新函数,传入要相加的数字。
  3. fmt.Println(...):打印返回结果。

逻辑串联(从调用到返回)

把整句 awaitAdd(2)(1, 2, 3) 连起来看:

  1. 第一步:awaitAdd(2) 创建并返回一个内部函数。
  2. 第二步:内部函数拿到 1,2,3 后先等待 2 秒。
  3. 第三步:遍历 1,2,3,计算总和 6
  4. 第四步:把 6 返回给 fmt.Println 输出。

等价写法(更容易看懂):

1
2
3
addAfter2s := awaitAdd(2)
result := addAfter2s(1, 2, 3)
fmt.Println(result)

输出结果(等待约 2 秒后):

1
6

常见错误

  1. 误把延时逻辑放在外层函数里,导致“创建闭包时延时”而不是“调用时延时”。
  2. 在循环中错误捕获变量,导致多个闭包共享同一最终值。
  3. 闭包层级过深且无注释,后期维护困难。
  4. 把两次调用写成一次传参(如 awaitAdd(2, 1, 2, 3)),会因为参数不匹配而编译失败。
使用 Hugo 构建
主题 StackJimmy 设计 由 Hobin 魔改
载入天数...载入时分秒...
发表了 0 篇文章 · 发表了 46 篇笔记 · 总计 2 万 5 千字(其中笔记 25104 字)