7. 函数
函数是执行特定任务的一段代码,程序通过将一段代码定义成函数,并为该函数指定一个函数名,这样即可在需要的时候多次调用这段代码。因此,函数是代码复用的重要手段。相比较于其他语言,Go 语言中的函数的使用更加广泛,函数可以说是 Go 语言中最核心的内容之一,是 Go 语言中的一等公民。
7.1. 函数入门
函数就是 Go 程序的重要组成部分,一个 Go 程序可以由很多个函数组成。
7.1.1. 理解函数
通俗来讲,函数就是将一段特定功能的代码放在一起,并为其取了一个名字,即函数名,在后续需要使用这段代码的功能时直接调用这个名字。正如在数组中使用到了取长度函数 len() 。
通常,函数可以接收零个或多个参数,也可以返回零个或多个值。从函数使用者的角度来看,函数就像一个黑盒,程序将零个或多个参数传入这个黑盒,该黑盒经过一番处理即可返回零个或多个值。具体流程如下图所示:

7.1.2. 定义函数和调用函数
在使用函数之前必须先定义函数,定义函数的语法格式如下:
func funcName(params1 Type1, ...) [(return1 Type2, ...)] {
// statements
}
注意:在 Go 语言中并没有严格的函数命名的要求,函数名可以由字母、数字和下划线组成,但不能以数字开头,且不能为 Go 语言中的关键字。但是 Go 官方建议使用驼峰命名法,且注意在 Go 语言中,首字母的大小写代表了可见性,这个后面会介绍到。
在 Go 语言中,声明函数时使用 func 关键字,对函数语法格式的详细说明如下:
funcName称为函数名:函数名由一个或多个有意义的单词连缀而成,函数名可以由字母、数字和下划线组成,但是不能以数字开头,且不能为 Go 语言中的关键字,建议使用驼峰命名法,且注意首字母的大小写。params称为形参列表:用于定义函数可以接收的参数。形参列表由多个形参名组成,多个形参名之间以英文逗号,隔开return称为返回值列表
如下代码给出了一个简单的函数的形式:
func sayHello() {
fmt.Println("Hello World")
}
func main() {
sayHello()
}
7.2. 参数
一个典型的函数定义如下,使用关键字 func,可以是无参的,也可以包含多个参数,上面的例子就是一个典型的无参函数,下面给出含参数的函数,如下代码:
func add(a int, b int) {
fmt.Println(a + b)
}
func main() {
a := 1
b := 2
add(a, b)
}
对于上面相同类型的参数,可以有下面的简写形式:
func add(a, b int) {
fmt.Println(a + b)
}
上述的代码中,若是参数个数不确定,如果要实现三个数的相加,还需要重新修改代码,能否有统一的方法?有,这便是可变参数,此时上面的代码可以修改成:
7.3. 返回值
Go 语言中函数的返回值有如下三种情况:
- 无返回值
- 单个返回值
- 多个返回值
上面已经给出了无返回值函数例子,对于有返回值的情况,需要标记出返回值类型,如下代码:
func add(a, b int) int {
return a+b
}
func main() {
a := 1
b := 2
fmt.Println(add(a, b))
}
也可以标记出具体的返回值,如下代码:
func add(a, b int) (sum int) {
sum = a+b
return
}
两种形式的对比,需要注意的是,在下面这种形式中,返回值和类型需要用 () 标记出来,同时,在代码里需要为返回值赋值,且最后直接 return。
Go 语言中支持多返回值,只需要在返回的时候标记好每一个返回值的类型,如下代码:
func div(a int, b int) (int, int) {
return a / b, a % b
}
func main() {
a := 5
b := 2
fmt.Println(div(a, b))
}
7.4. 函数也是值
在 Go 语言中,函数也是一个变量,如下代码:
func sayHello() {
fmt.Println("Hello World")
}
func main() {
// 将函数当成一个变量赋值给另一个变量
a := sayHello
// 调用
a()
}
7.4.1. 函数作为参数
函数可以作为另一个函数的参数,如下代码:
func cal(a int, b int, op func(int, int) int) int {
return op(a, b)
}
func add(a int, b int) int {
return a + b
}
func main() {
a := 1
b := 2
res := cal(a, b, add)
fmt.Println(res)
}
函数 add 作为另一个函数 cal 的第三个参数,参数的类型为 func(int, int) int。
7.4.2. 函数作为返回值
函数还可以作为另一个函数的返回值,如下代码:
func cal(s string) func(int, int) int {
if s == "add" {
return func(a int, b int) int {
return a + b
}
} else {
return func(a int, b int) int {
return a - b
}
}
}
func main() {
a := 1
b := 2
res := cal("add")
fmt.Println(res(a, b))
}
函数 cal() 的输出是一个函数,这个函数的类型为 func(int, int) int。我们可以通过下面的代码验证其类型:
func main() {
a := 1
b := 2
res := cal("add")
fmt.Printf("%T\n", res) // func(int, int) int
}
7.5. 匿名函数和闭包
7.5.1. 匿名函数
匿名函数,顾名思义,也就是没有函数名的函数,如下:
func() {
fmt.Println("Hello World")
}
那么怎么调用匿名函数呢,有两种方法,一种是直接执行,另一种将匿名函数当成一个变量,如下代码:
func main() {
// 直接执行
func() {
fmt.Println("Hello World")
}()
// 将匿名函数当成一个变量赋值给另一个变量
a := func() {
fmt.Println("Hello World")
}
// 调用
a()
}
7.5.2. 闭包
什么是闭包?一般会认为闭包是“匿名函数捕获了外部变量”。如何理解这句呢?先看一个例子:
func count() func() int {
n := 0
return func() int {
n += 1
return n
}
}
func main() {
// 将返回值赋值给变量 c
c := count()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
}
让我们先来分析下这段代码中的执行流程:
- 首先,函数
count()的返回值是一个匿名函数,函数的类型为func() int。我们将这个匿名函数赋值给变量 c; - 调用该匿名函数,并执行,此时,发现匿名函数中有一个变量 n,这个变量 n 是在匿名函数的外部定义的,这里就称为匿名函数捕获了外部变量,这就是闭包,调用一次给 n 加 1;
上面的例子是闭包的一个经典的例子,叫做计数器。
7.6. 本章小结
本章介绍了 Go 语言中的一等公民——函数。除了常规的定义以及使用方法之外,在 Go 语言中赋予了函数更多的用法,如多返回值,函数作为另一个函数的参数,函数作为另一个函数的返回值,匿名函数以及闭包等等,掌握这些方法能够更有效的编写 Go 程序。