基础知识二:函数与闭包

函数

在go中,既可以在包中定义函数,也可以定义结构体的函数,对于后者,函数接收者(结构体对象)是它的第一个参数。他们都会在运行时被构造成funcval对象,而闭包就是含有捕获变量的funcval对象
如结构体

type A struct {
    Name string
}
func (a A) GetName() string {
    return a.Name 
}
func (a *A) SetName() {
    a.Name = "Hello " + a.Name
}

这样来访问GetName方法

func TestFunc1(t *testing.T) {
    f := A.GetName
    a := A{Name: "a"}
    f(a)
}

在内存中的结构


内存中的结构

另一种调用方式

func TestFunc1(t *testing.T) {
    a := A{Name: "a"}
    fmt.Println(a.GetName())
}

在编译时,会被编译成A.GetName(a);在编码过程中,使用对象调用指针函数,指针调用对象函数,仍然会正常运行,其实是go提供的语法糖,在编译时会矫正

func TestFunc2(t *testing.T) {
    a := A{Name: "a"}
    a.SetName() // 编译为(&a).SetName(),运行时执行*A.SetName(&a)
    fmt.Println(a.Name) // 输出 Hello a
    pa := &a
    fmt.Println(pa.GetName()) // 输出 Hello a,编译为(*pa).GetName(),运行时执行A.GetName(*pa)
}

闭包有3个特点

  1. 变量在函数外部定义,在函数内部被引用,该变量为捕获变量
  2. 脱离了形成闭包的上下文,闭包函数也能使用捕获变量
  3. 闭包要求捕获变量在函数内部与外层函数表现一致,即捕获变量会在堆上分配,栈中只是存储了地址,这样,外层函数操作的变量与捕获的变量是同一个

内部运行原理

  1. 闭包函数的指令在编译时生成
  2. 由于闭包对象需要保存捕获变量,需要在运行时创建闭包对象funcVal
  3. 在运行时捕获变量在堆上分配,栈中只存储了变量地址,这样,外层函数操作的变量与捕获的变量时同一个
例子
func create() (fs [2]func()) {
    for i:=0; i<2; i++ {
        fs[i] = func() {
            fmt.Println(i)
        }
    }
    return
}

func TestClosure(t *testing.T) {
    fs := create()
    for i:=0; i

image.png

所以调用create方法后,实际i的值为2了,所以调用fs[i]时,打印的都是2

你可能感兴趣的:(基础知识二:函数与闭包)