go 函数详解 | 青训营笔记

94 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

(内容根据字节跳动青训营课程内容以及自己的理解编写)

近期将日更这几个主题的文章,欢迎关注!

  • 全面理解go协程
  • channel通信
  • Kitex
  • Hertx
  • Gorm
  • go的测试环节

函数初步认识

在go里面,函数也是一个类型,类似于js 函数式编程

我们打印一下函数的类型

func main() {
   // 注意这里是打印类型,不能加括号,函数加括号就调用了
   fmt.Printf("%T\n", add1)
   fmt.Printf("%T\n", add2)
}

func add1() {
}

func add2(x, y int) int {
   return x + y
}

image.png

发现可以打印出add函数的类型

既然函数是类型的话,是不是可以赋值?

答案是可以的

func main() {
   // 注意这里是打印类型,不能加括号,函数加括号就调用了
   fmt.Printf("%T\n", add1)
   fmt.Printf("%T\n", add2)

   // 赋值给函数
   var f1 func(int, int) int
   f1 = add2
   fmt.Println(f1(1, 2))

   // 也可以这么写
   f2 := add2
   fmt.Println(f2(1, 2))
}

func add1() {
}

func add2(x, y int) int {
   return x + y
}

image.png

匿名函数

相当于在定义的时候给出函数实现

func main() {
   // 匿名函数
   f1 := func() {
      fmt.Println("这是一个匿名函数")
   }

   f1()
}

image.png

也可以直接加一个括号执行! 后面的协程你会发现,每个协程定义完后面都会带个括号

func main() {
   // 匿名函数
   func() {
      fmt.Println("这是一个匿名函数")
   }()

}

image.png

匿名函数可以正常返回值

func main() {
   // 匿名函数
   ans := func(a, b int) int {
      return a + b
   }(1, 2)

   fmt.Println(ans)

}

image.png

高阶函数和回调函数

高阶函数就是接收了一个函数作为参数的函数

回调函数就是作为另一个函数的参数

举个例子:

package main

import "fmt"

func main() {
   fmt.Println(operation(1, 2, add))
}

// 高阶函数
func operation(a, b int, f func(a, b int) int) int {
   return f(a, b)
}

// 回调函数
func add(a, b int) int {
   return a + b
}

image.png

还可以这么把参数的方法写成匿名函数的样子:


func main() {
   fmt.Println(operation(1, 2, func(a, b int) int { // 匿名实现add函数
      return a + b
   }))
}

// 高阶函数
func operation(a, b int, f func(a, b int) int) int {
   return f(a, b)
}

image.png

闭包

闭包详解

image.png

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

Go语言是支持闭包的,这里只是简单地讲一下在Go语言中闭包是如何实现的。
下面我来将之前的JavaScript的闭包例子用Go来实现。

package main

import (
    "fmt"
)

func a() func() int {
    i := 0
    b := func() int {
        i++
        fmt.Println(i)
        return i
    }
    return b
}

func main() {
    c := a()
    c()
    c()
    c()

    a() //不会输出i
}

输出结果:

    1
    2
    3

闭包复制的是原对象指针,这就很容易解释延迟引用现象。

package main

import "fmt"

func test() func() {
    x := 100
    fmt.Printf("x (%p) = %d\n", &x, x)

    return func() {
        fmt.Printf("x (%p) = %d\n", &x, x)
    }
}

func main() {
    f := test()
    f()
}

输出:

    x (0xc42007c008) = 100
    x (0xc42007c008) = 100

外部引用函数参数局部变量

package main

import "fmt"

// 外部引用函数参数局部变量
func add(base int) func(int) int {
    return func(i int) int {
        base += i
        return base
    }
}

func main() {
    tmp1 := add(10)
    fmt.Println(tmp1(1), tmp1(2))
    // 此时tmp1和tmp2不是一个实体了
    tmp2 := add(100)
    fmt.Println(tmp2(1), tmp2(2))
}

返回2个闭包

package main

import "fmt"

// 返回2个函数类型的返回值
func test01(base int) (func(int) int, func(int) int) {
    // 定义2个函数,并返回
    // 相加
    add := func(i int) int {
        base += i
        return base
    }
    // 相减
    sub := func(i int) int {
        base -= i
        return base
    }
    // 返回
    return add, sub
}

func main() {
    f1, f2 := test01(10)
    // base一直是没有消
    fmt.Println(f1(1), f2(2))
    // 此时base是9
    fmt.Println(f1(3), f2(4))
}

资料

函数闭包