第四章(函数)
- go函数特点
- 无须前置声明
- 不支持命名嵌套定义
- 不支持同名函数重载
- 不支持默认参数
- 支持不定长变参
- 支持多返回值
- 支持命名返回值
- 支持匿名函数和闭包
- func只能判断其是否为nil,不支持其它比较操作
package main
func main() {
println(a == nil) //false
println(a == b) //invalid operation: a == b (func can only be compared to nil)
}
func a() {
}
func b() {
}
-
init()函数先于main函数执行,用于初始化
- init函数没有输入参数、返回值
- 每个包可以有多个init函数,多个init函数执行顺序go没有明确定义(注意不可依赖init函数执行顺序)
-
不管是指针、引用类型、还是其它类型参数,都是值拷贝传递(pass-by-value)
-
变参本质就是一个切片。只能接收一到多个同类型参数,且必须放在列表尾部。
func main() {
test("abc", 1, 2, 3, 4)
}
func test(s string, a ...int) {
fmt.Printf("%T,%v\n", a, a) //[]int,[1 2 3 4]
}
- 将切片作为变参时,须进行展开操作。如果是数组,先将其转换为切片
func main() {
a := [3]int{1, 2, 3}
test(a[:]...) //先转换为切片,再展开
}
//可变参数
func test(a ...int) {
fmt.Printf("%T,%v\n", a, a) //[]int,[1 2 3]
}
- 命名返回值和参数一样,可当作函数局部变量使用,最有由return隐式返回
func main() {
res, err := div(2, 0)
if nil != err {
println("err")
os.Exit(-1)
}
println(res)
}
func div(x, y int) (z int, err error) {
if 0 == y {
err = errors.New("division by zero")
return //隐式返回: 0,err
}
z = x / y
return //隐式返回:z,nil
}
- 闭包
- 闭包是在其词法上下文中引用了自由变量的函数,或者说是函数和其引用的环境的组合体
eg
func main() { f := test(0x100) f() } func test(x int) func() { println(&x) //0xc000055f68 return func() { println(&x, x) //0xc000055f68 256 } }
- 粗浅理解:闭包就是返回的匿名函数中引用了目标函数的变量,在main函数中执行时,可以正确读取变量的值。这种现象叫闭包
- 闭包-“延迟求值”
func main() {
for _, f := range test() {
f()
}
//0xc000102000 2
//0xc000102000 2
}
func test() []func() {
var s []func()
for i := 0; i < 2; i++ {
s = append(s, func() {
println(&i, i)
})
}
return s
}
上面输出的结果是2,因为添加操作仅仅是将匿名函数放入切片,并未执行。因此,当main执行这些函数时,读取的是环境变量i最后一次循环的值2
如果想输出1,2那么给一个中间变量就oj8k了
func main() {
for _, f := range test() {
f()
}
//0xc000018050 0
//0xc000018058 1
}
func test() []func() {
var s []func()
for i := 0; i < 2; i++ {
a := i
s = append(s, func() {
println(&a, a)
})
}
return s
}
-
defer函数(延迟调用函数)向当前函数注册稍后执行的函数调用。
-
错误处理
eg
func main() { z, err := div(5, 0) if err == errDivZero { log.Fatalln(err) //2023/01/05 23:12:27 division by zero } println(z) } var errDivZero = errors.New("division by zero") func div(x, y int) (int, error) { if 0 == y { return 0, errDivZero } return x / y, nil }
-
panic,recover类型try/catch
- 连续调用panic,仅最后一个会被recover捕获
func main() { defer func() { if err := recover(); err != nil { //捕获错误 log.Fatalln(err) } }() panic("i am panic") println("here") //永远不会执行 }
- panic会立即中断当前函数流程,执行延迟调用。而在延迟调用函数中,recover可捕获并返回panic提交的错误对象
- 无论是否recover,所有延迟调用都会被执行。panic要么被外层捕获,要么导致进程崩溃