defer表达式的执行次序

defer表达式的执行次序

defer表达式的执行次序 #

defer 关键字后面的表达式,是在将 deferred 函数注册到 deferred 函数栈的时候进行求值的。我们用一个典型的例子来说明一下 defer 后表达式的求值时机:

func foo1() {
    for i := 0; i <= 3; i++ {
        defer fmt.Println(i)
    }
}
func foo2() {
    for i := 0; i <= 3; i++ {
        defer func(n int) {
            fmt.Println(n)
        }(i)
    }
}
func foo3() {
    for i := 0; i <= 3; i++ {
        defer func() {
            fmt.Println(i)
        }()
    }
}
func main() {
    fmt.Println("foo1 result:")
    foo1()
    fmt.Println("\nfoo2 result:")
    foo2()
    fmt.Println("\nfoo3 result:")
    foo3()
}

foo1 中 defer 后面直接用的是 fmt.Println 函数,每当 defer 将 fmt.Println 注册到 deferred 函数栈的时候,都会对 Println 后面的参数进行求值。根据上述代码逻辑,依次压入 deferred 函数栈的函数是:

fmt.Println(0)
fmt.Println(1)
fmt.Println(2)
fmt.Println(3)

因此,当 foo1 返回后,deferred 函数被调度执行时,上述压入栈的 deferred 函数将以 LIFO 次序出栈执行,这时的输出的结果为:

3
2
1
0

foo2 中 defer 后面接的是一个带有一个参数的匿名函数。每当 defer 将匿名函数注册到 deferred 函数栈的时候,都会对该匿名函数的参数进行求值。根据上述代码逻辑,依次压入 deferred 函数栈的函数是:

func(0)
func(1)
func(2)
func(3)

因此,当 foo2 返回后,deferred 函数被调度执行时,上述压入栈的 deferred 函数将以 LIFO 次序出栈执行,因此输出的结果为:

3
2
1
0

foo3 中 defer 后面接的是一个不带参数的匿名函数。根据上述代码逻辑,依次压入 deferred 函数栈的函数是:

func()
func()
func()
func()

所以,当 foo3 返回后,deferred 函数被调度执行时,上述压入栈的 deferred 函数将以 LIFO 次序出栈执行。匿名函数会以闭包的方式访问外围函数的变量 i,并通过 Println 输出 i 的值,此时 i 的值为 4,因此 foo3 的输出结果为:

4
4
4
4

通过这些例子,我们可以看到,无论以何种形式将函数注册到 defer 中, deferred 函数的参数值都是在注册的时候进行求值的。

Viewpoint #

From #

23|函数:怎么让函数更简洁健壮?