1. 前言
我们的go文档 开始章节,输出hello world!里,就有一个main() 函数。 对于函数我们并不陌生,函数是基本的代码块,是用于执行一个任务的,Go 语言实现功能程序最少都会有个 main() 函数。 下面就让我们来看看go函数的奥妙!
2. 函数定义
函数的定义格式:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数由 func 开始声明,声明包括 函数名,参数列表, 返回值列表和函数体。如函数无参数、无返回就可忽略, 函数的左大括号只能和函数名在同一行。
3. 函数参数与返回值
参数的类型是在变量后面,如果多个参数同类型,可只保留最后一个参数类型。 函数有返回值时,必须有终止语句,否则会编译报错。
package main
func main() {
b := say("hello ", "world!")
println(b)
}
// x,y参数类型相同可以保留最后一个
func say(x, y string) string {
a := x + y
return a
}
输出:
hello world!
3.1. 值传递和引用传递
值传递:将实际参数复制一份传递到函数中,在函数里对参数修改,不会影响到实际参数
引用传递:将实际参数的地址传递到函数中,在函数中对参数修改,影响到实际参数。
package main
import "fmt"
func main() {
var x, y = 1, 2
fmt.Printf("原始 x:%d,y:%d \n", x, y)
value(x,y)
fmt.Printf("值传值后 x:%d,y:%d \n", x, y)
reference(&x, &y)
fmt.Printf("引用传值后 x:%d,y:%d", x, y)
}
// 值传值
func value(x,y int) {
x++
y++
}
// 引用传值
func reference(x, y *int) {
*x++
*y++
}
输出:
原始 x:1,y:2
值传值后 x:1,y:2
引用传值后 x:2,y:3
3.2. 传递变长参数
函数最后一个参数是 ...type 的形式,这个函数就成为一个变长的参数,长度可以为 0,这样的函数称为变长函数。
go函数变长参数本质上就是切片。这个参数只能有一个,且必须是最后一个参数,在参数赋值时可以不用用一个一个的赋值,可直接传递一个数组或者切片。
func function_name(a, b, arg ...int) {}
package main
import (
"fmt"
)
func main() {
println(add("sum: %d", 1, 2, 3, 4))
}
func add(str string, n ...int) string {
var a int
for _, i := range n {
a += i
}
return fmt.Sprintf(str, a)
}
输出:
sum: 10
4. 闭包
闭包是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。
匿名函数可作闭包里内层函数使用,匿名函数是没有函数名的函数,可在一个函数体里定义匿名函数。 由一个不带函数名的函数声明和函数体组成。优点在于可直接使用函数内的变量,不用申明。
package main
func add() func() int {
i := 1
return func() int {
i += 1
return i
}
}
func main() {
// add1 为一个函数,i变量为1
add1 := add()
// 调用 add1 函数,i 变量自增 1 并返回
println(add1())
println(add1())
println(add1())
// 创建新的函数 add2,并查看结果
add2 := add()
println(add2())
println(add2())
}
输出:
2
3
4
2
3
5. defer
关键字 defer 用于延迟调用,直到函数 return 前才被执,多个defer按照先进后出执行,另外defer语句中的变量,在defer声明时就决定了。 通常用于释放某些已分配的资源。
package main
import "fmt"
func main() {
one()
}
func one() {
fmt.Printf("one\n")
defer two()
fmt.Printf("three \n")
}
func two() {
fmt.Printf("two")
}
输出:
one
three
two
- 先进后出
package main
func main() {
var arr [3]int
for i := range arr {
defer println(i)
}
}
输出:
2
1
0
- 在闭包上使用 defer
package main
func main() {
var arr [3]int
for i := range arr {
defer func() { println(i) }()
}
}
闭包用到的变量 i 在执行的时候已经变成2,所以输出全都是2
输出:
2
2
2
6. 递归函数
一个函数在其函数体内调用自身,就叫做递归函数。
- 递归实现1+2+...+100
package main
func main() {
a := add(100)
println(a)
}
func add (a int) int {
if a == 1 {
return 1
}
return a + add(a -1)
}
输出:
5050
7. 异常处理
go 异常处理,可使用 panic 抛出错误,recover 捕获错误。常用步骤,go抛出panic异常,在defer中通过recover捕获这个异常,然后正常处理。
panic go内置函数,会终止其后要执行的代码,抛出异常(类似php中的throw)
func panic(v interface{})
recover go内置函数,可获取通过panic传递的error,获取异常(类似php中的catch),多次调用时,只有第一次能获取值
func recover() interface{}
使用defer(异常时函数结束返回)、panic(抛出异常)、recover(获取异常)进行异常处理:
package main
import "fmt"
func main() {
add()
}
func add() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic("panic error!")
}
输出:
panic error!
- 实现类似 try catch 的异常处理
package main
import "fmt"
func main() {
Try(func() {
panic("test panic")
}, func(err interface{}) {
fmt.Println(err)
})
}
func Try(fun func(), handler func(interface{})) {
defer func() {
if err := recover(); err != nil {
handler(err)
}
}()
fun()
}
输出:
panic error!