Effective Go中关于defer的说明如下:
延迟函数的参数(如果函数是方法,则包括接收器)在defer执行时被计算,而不是在调用执行时计算。除了避免担心变量在函数执行时改变值之外,这意味着单个延迟调用站点可以延迟多个函数执行。以下是一个愚蠢的例子。
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
延迟函数按照后进先出的顺序执行,因此当函数返回时,该代码将导致 4 3 2 1 0
被打印出来。
Effective Go中关于defer的说明如下:
延迟函数的参数(如果函数是方法,则包括接收器)在defer执行时被计算,而不是在调用执行时计算。除了避免担心变量在函数执行时改变值之外,这意味着单个延迟调用站点可以延迟多个函数执行。以下是一个愚蠢的例子。
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
延迟函数按照后进先出的顺序执行,因此当函数返回时,该代码将导致 4 3 2 1 0
被打印出来。
看起来很连贯(另请参见“Defer,Panic和Recover”)
延迟调用按照后进先出的顺序执行,在包围函数返回之后执行。
此函数将打印“3210”:
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
defer
被评估的最后一次意味着i=3
,倒数第二次意味着i=2
以此类推。
每次"
defer
"语句执行时,函数值和调用的参数会像往常一样被评估并保存,但实际的函数体不会被执行。
defers
将在函数结束时被调用
是的,但它们的参数在循环运行时就被评估了。
在 "How golang's “defer” capture closure's parameter?" 中,您遇到了一个更棘手的 defer
情况,当与闭包 (function literal)一起使用时,详见"Why add “()
” after closure body in Golang?"。
在更深入的阅读中,规范还明确指出,参数是在 defer
语句运行时评估的,而不是在实际调用延迟函数时进行返回/恐慌处理时评估:
每次执行 "defer" 语句时,函数值和调用的参数都像通常一样被评估并重新保存,但实际的函数体不会被执行。
是的,这可能确实令人困惑,因为参数在一个时间被评估,而函数体在另一个时间运行。我也曾被它所困扰。
defer
开头的行时执行的情况,也就是在循环内部发生了五次。相反,"the call executes"是指当fmt.Printf("%d ", i)
被执行时的情况,即外围函数返回时。printf
将在循环后调用,但defer
在内部被调用),一切都符合其他答案中所解释的行为。
defers
会在函数结束时调用(而不是在 for 循环结束时)。 - jtuki