和其他语言中的函数相近,主要是将具有特定功能的一部分代码抽象成可复用的代码块

函数

Go语言中支持普通函数、匿名函数和闭包

函数定义

Go语言中定义函数使用func关键字,具体格式如下:

  1. func 函数名(参数)(返回值){
  2. 函数体
  3. }

函数调用

参数

类型简写

如果相邻变量的类型相同,则可以省略类型,如下:

  1. func IntSum(x,y int) int {
  2. return x + y
  3. }

可变参数

可变参数一般是指函数的数量不固定
可变参数一般通过在参数名后面加...来标识

注意:一般可变参数通常要作为函数的最后一个参数

比如:

  1. func intSum2(x ...int) int {
  2. fmt.Println(x) //x是一个切片
  3. sum := 0
  4. for _, v := range x {
  5. sum = sum + v
  6. }
  7. return sum
  8. }

返回值

多返回值

go语言中的函数支持多返回值,函数中如果有多个返回值,则必须使用()包裹起来

  1. func calc(x, y int) (int, int) {
  2. sum := x + y
  3. sub := x - y
  4. return sum, sub
  5. }

返回值命名

函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。

  1. func calc(x, y int) (sum, sub int) {
  2. sum = x + y
  3. sub = x - y
  4. return
  5. }

函数进阶

变量作用域

函数类型与变量

Go语言基础之函数 - 李文周的博客

高阶函数

高阶函数分为函数作为参数和函数作为返回值两部分。

函数作为参数

有点类似于函数式编程

  1. func add(x, y int) int {
  2. return x + y
  3. }
  4. func calc(x, y int, op func(int, int) int) int {
  5. return op(x, y)
  6. }
  7. func main() {
  8. ret2 := calc(10, 20, add)
  9. fmt.Println(ret2) //30
  10. }

函数作为返回值

  1. func do(s string) (func(int, int) int, error) {
  2. switch s {
  3. case "+":
  4. return add, nil
  5. case "-":
  6. return sub, nil
  7. default:
  8. err := errors.New("无法识别的操作符")
  9. return nil, err
  10. }
  11. }

匿名函数与闭包

匿名函数

顾名思义,匿名函数就是在声明函数的时候并没有给函数一个名称
匿名函数的定义格式如下:

  1. func(参数)(返回值){
  2. 函数体
  3. }

而由于没有函数名,因此需要保存到某个变量中或者直接立即执行

闭包

闭包指的是一个函数和其相关的引用环境而组成的实体。简单来说,闭包=函数+饮用环境

  1. func adder() func(int) int {
  2. var x int
  3. return func(y int) int {
  4. x += y
  5. return x
  6. }
  7. }
  8. func main() {
  9. var f = adder()
  10. fmt.Println(f(10)) //10
  11. fmt.Println(f(20)) //30
  12. fmt.Println(f(30)) //60
  13. f1 := adder()
  14. fmt.Println(f1(40)) //40
  15. fmt.Println(f1(50)) //90
  16. }

变量f是一个函数,并且引用了其外部作用域中的x变量,此时f就是一个闭包。
f的生命周期内,变量x也一直有效,
闭包进阶实例1:

  1. func adder2(x int) func(int) int {
  2. return func(y int) int {
  3. x += y
  4. return x
  5. }
  6. }
  7. func main() {
  8. var f = adder2(10)
  9. fmt.Println(f(10)) //20
  10. fmt.Println(f(20)) //40
  11. fmt.Println(f(30)) //70
  12. f1 := adder2(20)
  13. fmt.Println(f1(40)) //60
  14. fmt.Println(f1(50)) //110
  15. }

闭包进阶示例2:

  1. func makeSuffixFunc(suffix string) func(string) string {
  2. return func(name string) string {
  3. if !strings.HasSuffix(name, suffix) {
  4. return name + suffix
  5. }
  6. return name
  7. }
  8. }
  9. func main() {
  10. jpgFunc := makeSuffixFunc(".jpg")
  11. txtFunc := makeSuffixFunc(".txt")
  12. fmt.Println(jpgFunc("test")) //test.jpg
  13. fmt.Println(txtFunc("test")) //test.txt
  14. }

defer语句

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。

  1. func main() {
  2. fmt.Println("start")
  3. defer fmt.Println(1)
  4. defer fmt.Println(2)
  5. defer fmt.Println(3)
  6. fmt.Println("end")
  7. }

上述代码的输出结果如下:

  1. start
  2. end
  3. 3
  4. 2
  5. 1

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。

defer语句的执行时机

在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:defer.png

defer经典案例

  1. func f1() int {
  2. x := 5
  3. defer func() {
  4. x++
  5. }()
  6. return x
  7. }
  8. func f2() (x int) {
  9. defer func() {
  10. x++
  11. }()
  12. return 5
  13. }
  14. func f3() (y int) {
  15. x := 5
  16. defer func() {
  17. x++
  18. }()
  19. return x
  20. }
  21. func f4() (x int) {
  22. defer func(x int) {
  23. x++
  24. }(x)
  25. return 5
  26. }
  27. func main() {
  28. fmt.Println(f1())
  29. fmt.Println(f2())
  30. fmt.Println(f3())
  31. fmt.Println(f4())
  32. }