继续学Go了。在Go中也是有函数这么一个可复用的代码块。
Go语言中的函数也是支持普通函数,匿名函数,闭包等性质。并且在Go中,函数属于“一等公民”。
在Go中定义函数要使用func
关键字,格式如下:
1 | func 函数名(参数列表)(返回值) { |
这些定义都没什么不同,只是要注意在Go中更推荐使用小驼峰,使用其他的虽然不报错,但是不符合Go。
函数定义完了后,就可以在需要用的地方使用函数。
参数
类型简写
在Go中,如果参数的类型和相邻的参数类型相同,就可以省略类型。
1 | func fn1(a, b int) int { |
上述fn1
函数有两个参数,他们的类型都是int
,所以可以省略a的类型不写。
可变参数
可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...
来标识。
固定参数搭配可变参数使用时,可变参数要放在固定参数的后面。
1 | func intSum2(x ...int) int { |
本质上,函数的可变参数是通过切片来实现的。
返回值
Go语言中通过return
关键字向外输出返回值。
多返回值
Go语言中函数支持多返回值,函数如果有多个返回值时必须用()
将所有返回值包裹起来。
1 | func calc(x, y int) (int, int) { |
返回值命名
函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return
关键字返回。
例如:
1 | func calc(x, y int) (sum, sub int) { |
返回值补充
当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。
1 | func someFunc(x string) []int { |
作用域
全局变量
全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。
1 | package main |
局部变量
局部变量又分为两种: 函数内定义的变量无法在该函数外使用,例如下面的示例代码main函数中无法使用testLocalVar函数中定义的变量x:
1 | func testLocalVar() { |
同时:如果局部变量和全局变量重名,优先访问局部变量。
函数类型
定义函数类型
我们可以使用type
关键字来定义一个函数类型,具体格式如下:
1 | type calculation func(int, int) int |
上面语句定义了一个calculation
类型,它是一种函数类型,这种函数接收两个int类型的参数并且返回一个int类型的返回值。
简单来说,凡是满足这个条件的函数都是calculation类型的函数,例如下面的add和sub是calculation类型。
1 | func add(x, y int) int { |
所以add
和sub
都可以赋值给calcation类型的变量。
1 | var c calculation |
函数类型变量
我们可以声明函数类型的变量并且为该变量赋值:
1 | func main() { |
特殊用法
函数作为参数
函数可以作为参数:
1 | func add(x, y int) int { |
如上述的calc
函数就使用了op
函数作为参数,并且在内部调用了op
函数。
函数作为返回值
函数也可以作为返回值:
1 | func do(s string) (func(int, int) int, error) { |
匿名函数
匿名函数就是没有函数名的函数,匿名函数的定义格式如下:
1 | func(参数)(返回值){ |
匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:
1 | package main |
匿名函数多用于实现回调函数和闭包。
闭包
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境
。
首先我们来看一个例子:
1 | func adder() func(int) int { |
变量f
是一个函数并且它引用了其外部作用域中的x
变量,此时f
就是一个闭包。
在f
的生命周期内,变量x
也一直有效。
defer语句
Go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的逆序进行执行,也就是说,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
1 | func main() { |
执行截图:

由于defer
语句延迟调用的特性,所以defer
语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
defer执行时机
在Go语言的函数中return
语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer
语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:

内置函数
内置函数 | 介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
panic/recover
Go语言中目前没有异常机制,但是使用panic/recover
模式来处理错误。
panic
可以在任何地方引发,但recover
只有在defer
调用的函数中有效。
1 | func funcA() { |
如上案例,在输出时,则会报错,因为func
引发了一个panic
。
这时可以使用defer
+recover
来处理错误。
1 | func funcB() { |
recover()
必须搭配defer
使用。defer
一定要在可能引发panic
的语句之前定义。