关于闭包:go的defer和闭包例子说明非内部实现

48次阅读

共计 2298 个字符,预计需要花费 6 分钟才能阅读完成。

用几个例子阐明 golang 的闭包函数,联合 defer 应用,配合对应代码及文末总结。

函数 阐明 输入
e1 defer 调用,相当于是拿到了以后 err 变量的快照,即注册 defer 函数的时候,将当下 err 的值塞入到 defer 中 start err1
e2 defer 调用,然而一个闭包函数,且闭包函数有传参,闭包捕捉以后 err 的值依然是 start err2(闭包捕捉的是变量值的拷贝),且闭包内的值变量扭转不会影响内部 err 的值(详见见 e5) start err2
e3 defer 调用,闭包内的变量和匿名函数外的变量是专用的,没有传递形参,没有传递形参,与上下文共享 defer3 error
e4 defer 调用,在函数 e4 中,当你将 err 作为参数传递给闭包函数时,实际上是创立了一个闭包函数的正本,这个正本在闭包外部独立于内部作用域。这种行为是因为闭包在捕捉内部变量时,会将内部变量的以后值复制到闭包外部,造成一个闭包环境,当初了解了闭包的概念了吧。具体来说,在 defer 语句执行的时候,闭包函数会将 err 的以后值(即 “start err4″)复制到闭包外部的参数中。之后,无论内部作用域的 err 是否产生扭转,闭包外部的参数值都会放弃不变,因为闭包曾经捕捉了一个快照 start err4
e5 传值的状况下,闭包内的值变量扭转不会影响内部 err 的值,(相互独立) now err is start err5 start err5CHANGE ME
e6 闭包没有传值,拿到的 err 是最初赋值的, now err is start err6 defer6 error CHANGE ME
package main

import (
    "errors"
    "fmt"
)

func e1(){err := errors.New("start err1")
    defer fmt.Println(err)
    err = errors.New("defer1 error")
    return
}

func e2(){err := errors.New("start err2")
    defer func(e error) {fmt.Println(e)
    }(err)
    err = errors.New("defer2 error")
    return
}

func e3(){err := errors.New("start err3")
    // 闭包内的变量和匿名函数外的变量是专用的,没有传递形参,没有传递形参,与上下文共享
    defer func() {fmt.Println(err)
    }()
    err = errors.New("defer3 error")
    return
}

func e4(){
    var err error
    err = errors.New("start err4")
    // 闭包内的变量和匿名函数外的变量是专用的,然而如果传了形参,那就和上文的共用了
    // 在函数 e4 中,当你将 err 作为参数传递给闭包函数时,实际上是创立了一个闭包函数的正本,这个正本在闭包外部独立于内部作用域。这种行为是因为闭包在捕捉内部变量时,会将内部变量的以后值复制到闭包外部,造成一个闭包环境
    // 具体来说,在 defer 语句执行的时候,闭包函数会将 err 的以后值(即 "start err4")复制到闭包外部的参数中。之后,无论内部作用域的 err 是否产生扭转,闭包外部的参数值都会放弃不变,因为闭包曾经捕捉了一个快照。defer func(err error) {fmt.Println(err)
    }(err)
    err = errors.New("defer4 error")
    return
}

func e5(){err := errors.New("start err4")

    defer func(err error) {err=errors.New(err.Error()+"CHANGE ME")
        fmt.Println(err)
    }(err)
    fmt.Println("now err is",err)
    err = errors.New("defer5 error")
    return
}
func e6() {err := errors.New("start err6")

    defer func() {err = errors.New(err.Error() + "CHANGE ME")
        fmt.Println(err)
    }()

    fmt.Println("now err is", err)
    err = errors.New("defer6 error")
    return
}

func main(){e1()
    e2()
    e3()
    e4()
    e5()
    e6()}

变量作用域和闭包:

Go 语言中的变量作用域由代码块决定。变量在其定义的代码块内可见。
闭包是一个函数值,它能够捕捉其定义时四周的作用域内的变量。
闭包能够在定义之外被调用,依然拜访并批改捕捉的变量。

闭包和变量捕捉:

闭包函数能够捕捉内部作用域的变量。在闭包外部,它们能够拜访内部变量的值。
闭包捕捉的变量是其正本,即闭包外部应用的是变量值的拷贝。
批改闭包外部捕捉的变量不会影响内部作用域中的变量,除非你在闭包内间接批改内部作用域的变量。

闭包参数传递:

在闭包外部接管内部作用域的变量作为参数,能够使闭包操作内部作用域的变量。
应用闭包参数传递能够无效隔离闭包内外的变量,从而放弃可预测性。

在 defer 中的闭包:

当在 defer 语句中应用闭包时,闭包外部的变量会被“捕捉”并在 defer 执行时应用。
在闭包外部批改闭包捕捉的变量不会影响内部作用域中的变量,除非你间接批改内部作用域的变量。

总结:

闭包是一种弱小的概念,能够使函数领有状态并提早执行。
理解闭包如何操作变量作用域,以及它们如何捕捉和批改变量,是编写高效、清晰的 Go 代码的要害。
当在闭包中操作变量时,要留神变量作用域、捕捉的变量正本和对外部作用域的影响。

正文完
 0