Golang 中的闭包和defer

具体定义这里不做累述了,主要看看两则放在一起使用的情况.

闭包里面的参数

先看一段代码:

package main

import (
	"fmt"
)

func main() {
	fmt.Println(test())
}

func test() int {
	var i int
	f := func() {
		i += 1 //闭包是通过指针传递的,所以对i的修改相当于修改指针指向的内容.
		fmt.Println("i:", i)
	}
	f()
	f()
	fmt.Println("test:", i)
	return i
}
复制代码

打印结果:

D:\workspace\go\src\test>go run main.go
i: 1
i: 2
test: 2
2 
复制代码

因为闭包中对变量i是通过指针传递的,所以闭包里面对i的修改会直接修改i的值。

defer和普通函数配合

defer特性:

    1. 关键字 defer 用于注册延迟调用。
    2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
    3. 多个defer语句,按先进后出的方式执行。
    4. defer语句中的变量,在defer声明时就决定了。
复制代码

defer用途:

    1. 关闭文件句柄
    2. 锁资源释放
    3. 数据库连接释放
复制代码

代码1:

package main

func add(x, y int) (z int) {
	defer func() {
		println(z+9) // 输出: 212
	}()

	z = x + y
	return z + 200 // 执行顺序: (z = z + 200) -> (call defer) -> (return)
}

func main() {
	println(add(1, 2)) // 输出: 203
}
复制代码

结果【当函数写了返回值的时候

(z int),return z+20,会先运行z=z+20再运行defer,最后return

】:

D:\workspace\go\src\test>go run main.go
212
203 
复制代码

代码2:

package main

import (
	"fmt"
)

func main() {
	fmt.Println(a())
}

func a() int {
	var i int
	defer add(i) //延迟调用add(),在程序计数器走到defer时,会把defer右边最外层函数的参数计算完毕,也就是此时的i是多少就是多少
	i += 100
	return i
}

func add(i int) {
	i += 1
	fmt.Println(i)
} 
复制代码

结果:

D:\workspace\go\src\test>go run main.go
1
100 
复制代码

defer和闭包函数配合

代码:

package main

import (
	"fmt"
)

func main() {
	fmt.Println(b())
}

func b() int {
	var i int
	defer func() {
		add(i) //可以理解为传的是i的指针,{}里面的函数体入栈,i此时不计算,最后是什么就是什么
	}()
	i += 100
	return i //return 之前运行defer,此时的defer里面的i是100,调用add变为101
}

func add(i int) {
	i += 1
	fmt.Println(i)
} 
复制代码

结果:

D:\workspace\go\src\test>go run main.go
101
100 
复制代码

这里和普通函数产生不同是因为,如果你在定义defer的时候,就要将defer后面的函数参数等入栈,等到ruturn之前的时候出栈执行,a方法中是将i的拷贝直接入栈,b方法中通过一个闭包调用,实际上将i的指针传递给闭包,闭包读取值拷贝给add。

在程序计数器走到defer时,会把defer右边最外层函数的参数计算完毕,并传递进函数里,但不会执行函数体**(匿名函数的{}里面)**的代码直到包裹defer的函数返回。

另外我们经常说defer定义的顺序跟执行的顺序相反也是因为栈中先入后出的原因.

总结

其实理解defer 闭包这些只要理解以下几点就会彻底明白

  1. 闭包是通过指针操作母函数中的变量

  2. defer 定义的过程就相当生成一个函数体然后将它压入栈,等到return的时候出栈执行

  3. defer 执行是在return 之前执行。这里要注意的是如果没有声明返回变量,需要先声明一个返回值然后赋值给它返回。

参考:studygolang.com/articles/15…

Golang defer详解

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享