和其他语言中的函数相近,主要是将具有特定功能的一部分代码抽象成可复用的代码块
函数
函数定义
Go语言中定义函数使用func
关键字,具体格式如下:
func 函数名(参数)(返回值){
函数体
}
函数调用
参数
类型简写
如果相邻变量的类型相同,则可以省略类型,如下:
func IntSum(x,y int) int {
return x + y
}
可变参数
可变参数一般是指函数的数量不固定
可变参数一般通过在参数名后面加...
来标识
注意:一般可变参数通常要作为函数的最后一个参数
比如:
func intSum2(x ...int) int {
fmt.Println(x) //x是一个切片
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}
返回值
多返回值
go语言中的函数支持多返回值,函数中如果有多个返回值,则必须使用()
包裹起来
func calc(x, y int) (int, int) {
sum := x + y
sub := x - y
return sum, sub
}
返回值命名
函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return
关键字返回。
func calc(x, y int) (sum, sub int) {
sum = x + y
sub = x - y
return
}
函数进阶
变量作用域
函数类型与变量
高阶函数
函数作为参数
有点类似于函数式编程
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) //30
}
函数作为返回值
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
}
}
匿名函数与闭包
匿名函数
顾名思义,匿名函数就是在声明函数的时候并没有给函数一个名称
匿名函数的定义格式如下:
func(参数)(返回值){
函数体
}
闭包
闭包指的是一个函数和其相关的引用环境而组成的实体。简单来说,闭包=函数+饮用环境
func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder()
fmt.Println(f(10)) //10
fmt.Println(f(20)) //30
fmt.Println(f(30)) //60
f1 := adder()
fmt.Println(f1(40)) //40
fmt.Println(f1(50)) //90
}
变量f是一个函数,并且引用了其外部作用域中的x
变量,此时f
就是一个闭包。
在f
的生命周期内,变量x
也一直有效,
闭包进阶实例1:
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder2(10)
fmt.Println(f(10)) //20
fmt.Println(f(20)) //40
fmt.Println(f(30)) //70
f1 := adder2(20)
fmt.Println(f1(40)) //60
fmt.Println(f1(50)) //110
}
闭包进阶示例2:
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
}
defer语句
Go语言中的defer
语句会将其后面跟随的语句进行延迟处理。在defer
归属的函数即将返回时,将延迟处理的语句按defer
定义的逆序进行执行,也就是说,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
func main() {
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
上述代码的输出结果如下:
start
end
3
2
1
由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
defer语句的执行时机
在Go语言的函数中return
语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer
语句执行的时机就在返回值赋值操作后,RET
指令执行前。具体如下图所示:
defer经典案例
func f1() int {
x := 5
defer func() {
x++
}()
return x
}
func f2() (x int) {
defer func() {
x++
}()
return 5
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
}