1、函数声明
- 函数声明格式
func 函数名([参数列表]) [返回值] {
函数体
}
-
声明函数的两种方式
- 全局声明函数
func sum(a, b int) int { return a + b }- 将函数作为变量值
func main() { var sum = func(a, b int) int { return a + b } fmt.Println(sum(1, 3)) } -
注意:
在Go中函数没有重载,如果两个函数名相同,入参返回值不一样,会编译不通过。
因为在Go的理念中,如果两个函数参数不同,则应该为两个不同的函数,所以函数名不应该取一样的。
2、函数参数
- Go的参数可以不带名称,不过一般是在接口或函数类型声明的时候用,为了可读性,参数最好还是带上名称。
//声明函数类型
type ExWriter func(io.Writer) error
//声明接口
type Writer interface {
ExWrite([]byte) (int, error)
}
- 相同参数类型:参数类型相同,可只简写一个类型。
func sum(a,b int) int {//a,b参数类型相同,所以可只写一个int
return a + b
}
- 变长参数:可以接收多个参数,但必须声明在最后的参数
func sum(a, b int, other ...int) int {//other为变长参数
for i, v := range other {
fmt.Println(i, v)
}
return a + b
}
-
注意:Go 中的函数参数是传值传递,即在传递参数时会拷贝实参的值。
切片或Map数据结构本质上都是指针,所以传递时不会复制大量的内存。
3、返回值
- 无返回值:Go中参数无返回值没有void或其他关键字,只要不写返回值类型就行
func sum(a, b int) {
fmt.Println(a + b)
}
- 一个返回值
func sum(a, b int) int { //括号外面的int就是返回值类型
return a + b
}
- 多个返回值:Go的特点之一,函数允许有多个返回值
func main() {
value, str := sum(1, 3)//接收多个返回值
fmt.Println(value, str)
}
//两个返回值,一个int,一个string
func sum(a, b int) (int, string) {
return a + b, "hello"
}
- 具名返回:Go中可以给返回值先取名,然后在后续的
return中,无需指定返回哪些值,默认返回取名的返回值。func sum(a, b int) (value int, str string) { value = a + b str = fmt.Sprintf("%d", value) return //未指定返回的值,函数默认把value和str返回 }- 如果
return指定了返回的值,则函数有优先返回return指定的值。
func sum(a, b int) (value int, str string) { value = a + b str = fmt.Sprintf("%d", value) return 10, "hello" //return指定了其他的值,函数就不会返回value和str的值 } - 如果
4、匿名函数
-
声明函数时没有函数名称,所以称为匿名函数,因没有函数名称,所以只能在函数声明时调用该函数
func main() { value := func(a, b int) int { return a + b }(1, 2)//直接调用了该匿名函数 fmt.Println(value) } -
最常的使用场景:当A函数作为B函数的参数时(
Go中函数可以作为参数),B被调用,传入函数A时,传入的函数名称就不需要了。import "fmt" func main() { value := add(2, func(c, d int) int {//匿名函数作为参数传入函数中。 return c + d }) fmt.Println(value) } func add(a int, b func(c, d int) int) int { //b参数是一个函数 return b(a, 2) }
5、闭包
- 闭包例子
import "fmt" func main() { grow := Exp(2) //此时的grow是Exp返回的函数 for i := range 10 { value := grow() //调用grow时,会使用到Exp函数定义的e变量 fmt.Printf("2^%d=%d\n", i, value) } } func Exp(n int) func() int { e := 1 return func() int { temp := e e *= n return temp } }- 在grow赋值时,Exp函数的作用域内定义了e变量;当grow赋值结束后,正常e变量应该没用了,会释放内存,但Exp返回的函数,使用的了e变量,相当于被grow使用了,所以e变量无法释放内存(逃逸到堆上),Exp函数声明周期已经结束,到e变量和n变量还没结束(被grow使用),所以grow函数是一个闭包函数。
6、延迟调用
- 延迟调用使用
defer关键字,用defer关键字标识的函数,会在延迟到最后依序执行。
import "fmt"
func main() {
fmt.Println(1)
defer deferFunc()
fmt.Println(2)
}
func deferFunc() {
fmt.Println(3)
}
defer底层的内存结构是一个栈,如果有定义多个defer执行逻辑,先定义的后执行。
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
defer fmt.Println(4)
defer fmt.Println(5)
fmt.Println("start")
}
// 执行结果
start
5
4
3
2
1
-
defer参数预处理import "fmt" func main() { fmt.Println("start") defer multiplication(sum(1, 2), 3) fmt.Println("end") } func sum(a, b int) int { fmt.Println("sum execute。。。。。。") return a + b } func multiplication(c, d int) int { fmt.Println("multiplication execute。。。。。。") return c * d }- 按
defer的执行逻辑,预期的执行结果应该为:
start end sum execute。。。。。。 multiplication execute。。。。。。- 实际的执行结果为:
start sum execute。。。。。。 end multiplication execute。。。。。。-
原因解析:
defer会对直接作用的函数参数进行预处理,也就是说,defer定义时,保证直接作用的函数参数就是当时的参数值,后续的参数值的更改不会影响defer函数的执行。import "fmt" func main() { fmt.Println("start") a, b := 1, 2 defer sum(a, b)//在栈中保存不只函数,还有当前的参数值 a = 3 b = 4 sum(a, b) } func sum(a, b int) { fmt.Println("sum param:", a, b) } //执行结果 start sum param: 3 4 sum param: 1 2
- 按