defer 机制
defer
机制一般有两个考点,第一个是多defer
的执行顺序,第二个就是defer func(){...}()
和 defer func(a) {...}(a)
的区别。
1.1 执行顺序
题目:下面这段代码的输出结果是什么?
package main
import "fmt"
func out(str string) {
fmt.Println(str)
}
func main() {
str := "str_0"
defer out(str)
for i := 1;i<11;i++ {
str = fmt.Sprintf("str_%v",i)
defer out(str)
}
defer out(str)
}
这一题主要考的是你对defer
执行顺序的理解程度,相对来说比较简单。只要应试者知道defer
执行类似于堆栈结构,符合先入后出,后入先出的逻辑即可正确作答。
答题思路:
- 首选我们知道了
defer
执行顺序遵循堆栈结构的先入后出,后入先出。 - 分析上文代码,发现
defer
入栈顺序为:defer out("str_0")->defer out("str_1")->defer out("str_2")->...->defer out("str_10")->defer out("str_10")
- 反向整理后得到正确的输出顺序。
正确答案:
str_10
str_10
str_9
str_8
str_7
str_6
str_5
str_4
str_3
str_2
str_1
str_0
1.2 defer func(){...}()
和 defer func(a) {...}(a)
的区别
题目:下面这段代码的输出结果是什么?
package main
import "fmt"
func main() {
str := "str_0"
defer func(str string){
fmt.Println(str)
}(str)
for i := 1;i<11;i++ {
str = fmt.Sprintf("str_%v",i)
defer func() {
fmt.Println(str)
}()
defer func(str string) {
fmt.Println(str)
}(str)
}
defer func(){
fmt.Println(str)
}()
}
这一题可以算作是上一题的进阶题,既考了defer
的执行顺序,又对调用defer
传入参数的方式进行了考察,要解答这一题,仅仅知道defer
遵循堆栈先入后出仍然不能正确的达出,还需要应试者知道 defer func(){...}()
和 defer func(a) {...}(a)
的区别。
defer func(){...}()
和 defer func(a) {...}(a)
的区别最主要的是变量的作用域不一样,我们可以抛开defer
不聊,只聊func(){...}()
和func(a) {...}(a)
。
拆开来看,很容易发现这两个方法实际上就是两个闭包调用。这里就不具体展开了,我们直接引入闭包定义:内层函数引用外层函数名称空间中的变量即闭包。
即:闭包内可以同时引用内层函数和外层函数空间中的变量,我们可以理解为如果内层函数引用的变量在内层函数内没有定义则认为它使用的是外层函数空间的变量。然后我们就可以正式解题了。
解题思路:
- 题中有两种
defer
调用方法,defer func(){...}()
和defer func(str) {...}(str)
。 - 通过闭包定义,我们知道
defer func(){...}()
中引用的str
为外部空间的变量str
,使用str
时等于直接使用外部变量str
。 defer func(str) {...}(str)
中引用的str
由于是作为参数传进来的,等于内部对变量进行了重定义,所以使用的是传进来的参数。- 外部变量和传入参数的区别在于使用时外部变量的值为调用那一刻的值,而传入参数使用时值为传入那一刻的值。
- 配合上一题,整理出输入顺序。
正确答案:
str_10
str_10
str_10
str_9
str_10
str_8
str_10
str_7
str_10
str_6
str_10
str_5
str_10
str_4
str_10
str_3
str_10
str_2
str_10
str_1
str_10
str_0
1.2 进阶题
题目:下面这段代码的输出结果是什么?
package main
import "fmt"
func main() {
str := "str_0"
defer func(){
fmt.Println(str)
}()
for i := 1;i<11;i++ {
str = fmt.Sprintf("str_%v",i)
defer func() {
fmt.Println(str)
}()
}
defer func() {
fmt.Println(str)
}()
for i := 11;i<21;i++ {
str = fmt.Sprintf("str_%v",i)
defer func(str string) {
fmt.Println(str)
}(str)
}
}
上面这个题目就交给读者自己解答了,想要知道运行结果的可以在这个网站上输入代码并查看:https://glot.io/new/go