1. 函数的基本形式 1.1 形参与实参 1 2 3 4 5 6 7 func add (a int , b int ) int { c := a + b return c }
1 2 3 4 5 func add (a *int , b *int ) int { *a = *a + *b return *a }
1.2 可变参数 可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...
来标识。注意:可变参数通常要作为函数的最后一个参数,如下:
1 2 3 4 5 6 7 8 func intSum2 (x ...int ) int { fmt.Println(x) sum := 0 for _, v := range x { sum = sum + v } return sum }
调用上面的函数:
1 2 3 4 5 ret1 := intSum2() ret2 := intSum2(10 ) ret3 := intSum2(10 , 20 ) ret4 := intSum2(10 , 20 , 30 ) fmt.Println(ret1, ret2, ret3, ret4)
固定参数搭配可变参数使用时,可变参数要放在固定参数的后面,示例代码如下:
1 2 3 4 5 6 7 8 func intSum3 (x int , y ...int ) int { fmt.Println(x, y) sum := x for _, v := range y { sum = sum + v } return sum }
调用上述函数:
1 2 3 4 5 ret5 := intSum3(100 ) ret6 := intSum3(100 , 10 ) ret7 := intSum3(100 , 10 , 20 ) ret8 := intSum3(100 , 10 , 20 , 30 ) fmt.Println(ret5, ret6, ret7, ret8)
本质上,函数的可变参数是通过切片来实现的。
1.3 返回值 1 2 3 4 5 6 func calc (x, y int ) (int , int ) { sum := x + y sub := x - y return sum, sub }
1 2 3 4 5 6 func calc (x, y int ) (sum, sub int ) { sum = x + y sub = x - y return }
1 2 3 4 5 6 7 func someFunc (x string ) []int { if x == "" { return nil } ... }
1.4 作为参数与返回值 1 2 3 4 5 6 7 8 9 10 11 func add (x, y int ) int { return x + y }func calc (x, y int , op func (int , int ) int ) int { return op(x, y) }func main () { ret2 := calc(10 , 20 , add) fmt.Println(ret2) }
1 2 3 4 5 6 7 8 9 10 11 12 func do (s string ) (func (int , int ) int , error ) { switch s { case "+" : return add, nil case "-" : return sub, nil default : err := errors.New("无法识别的操作符" ) return nil , err } }
2. 函数类型与变量 可以使用type
关键字来定义一个函数类型,具体格式如下:
1 type calculation func (int , int ) int
上面语句定义了一个calculation
类型,它是一种函数类型,这种函数接收两个int类型的参数并且返回一个int类型的返回值。简单来说,凡是满足这个条件的函数都是calculation类型的函数,例如下面的add和sub是calculation类型。
1 2 3 4 5 6 7 func add (x, y int ) int { return x + y }func sub (x, y int ) int { return x - y }
add和sub都能赋值给calculation类型的变量。
1 2 var c calculation c = add
可以声明函数类型的变量并且为该变量赋值:
1 2 3 4 5 6 7 8 9 10 func main () { var c calculation c = add fmt.Printf("type of c:%T" , c) fmt.Println(c(1 , 2 )) f := add fmt.Printf("type of f:%T" , f) fmt.Println(f(10 , 20 )) }
3. 匿名函数与闭包 3.1 匿名函数 函数当然还可以作为返回值,但是在Go语言中函数内部不能再像之前那样定义函数了,只能定义匿名函数。匿名函数就是没有函数名的函数,匿名函数的定义格式如下:
匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:
1 2 3 4 5 6 7 8 9 10 11 12 func main () { add := func (x, y int ) { fmt.Println(x + y) } add(10 , 20 ) func (x, y int ) { fmt.Println(x + y) }(10 , 20 ) }
1 2 3 4 5 6 7 8 9 10 11 12 type user struct { name string hello func (name string ) string }func main () { ch := make (chan func (name string ) string , 0 ) ch <- func (name string ) string { return "hello" + name } }
匿名函数多用于实现回调函数和闭包。
3.2 闭包 闭包是引用了自由变量的函数,自由变量将和函数一起存在,即使离开了创造环境,闭包复制是原对象的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func sub () func () { i := 10 fmt.Printf("%p\n" ,&i) b := func () { fmt.Printf("%p\n" ,&i) i-- fmt.Println(i) } return b }func main () { b := sub() b() b() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func add (base int ) func (int ) int { return func (i int ) int { fmt.Printf("%p\n" ,&base) base += i return base } }func main () { t := add(10 ) fmt.println (t(1 ),t(2 )) t2 := add(100 ) fmt.println (t2(1 ),t2(2 )) }
4. 延迟处理defer
defer用于注册一个延迟调用,在函数返回之前调用,一般用于资源释放场景,如文件句柄释放,数据连接释放等。
如果一个函数里面有多个defer,则后注册先执行
defer 后可以跟一个func,如果func内部发生panic,会把panic暂时搁置,当其他defer执行完之后再执行这个
defer 后跟的是一条执行语句,则相关变量将在注册defer时被拷贝或计算
1 2 3 4 5 6 7 8 func deferExe () (i int ) { i = 9 defer func () { fmt.Printf("i=%d\n" , i) }() defer fmt.Printf("i=%d\n" , i) return 5 }
5. 异常处理 程序运行期间funcB
中引发了panic
导致程序崩溃,异常退出了。这个时候我们就可以通过recover
将程序恢复回来,继续往后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func funcA () { fmt.Println("func A" ) }func funcB () { defer func () { err := recover () if err != nil { fmt.Println("recover in B" ) } }() panic ("panic in B" ) }func funcC () { fmt.Println("func C" ) }func main () { funcA() funcB() funcC() }