继续学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的语句之前定义。
提供CDN加速/云存储服务
