go的defer和闭包(例子说明,非内部实现)

析构潮汐
• 阅读 1116

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

函数说明输出
e1defer调用,相当于是拿到了当前err变量的快照,即注册defer函数的时候,将当下err的值塞入到defer中start err1
e2defer 调用,但是一个闭包函数,且闭包函数有传参,闭包捕获当前err的值仍然是 start err2(闭包捕获的是变量值的拷贝),且闭包内的值变量改变不会影响外部err的值(详见见e5)start err2
e3defer 调用,闭包内的变量和匿名函数外的变量是公用的,没有传递形参,没有传递形参,与上下文共享defer3 error
e4defer 调用,在函数 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 代码的关键。
当在闭包中操作变量时,要注意变量作用域、捕获的变量副本和对外部作用域的影响。

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Souleigh ✨ Souleigh ✨
4年前
JS - 从执行上下文的角度来理解闭包
今天看到一篇关于闭包的文章,里面有这样一句话“就我而言对于闭包的理解仅止步于一些概念,看到相关代码知道这是个闭包,但闭包能解决哪些问题场景我了解的并不多”,这说的不就是我么,每每在面试中被问及什么是闭包,大部分情况下得到的答复是(至少我以前是)A函数嵌套B函数,B函数使用了A函数的内部变量,且A函数返回B函数,这就是闭包。而往往面试官想要听到的并不是这样的
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
thymeleaf在工作中遇到的问题及解决办法(四)
1、关于字符串拼接的问题       字符串拼接可以使用如下方式。<ahref""th:text"第${StartNo}页''共${countPage}页"       还有一种更优雅的方式,使用“||”减少了字符串的拼接,代码如下。<ahref""th:
Bill78 Bill78
4年前
Python 中的闭包
闭包定义:如果在一个内部函数里,对在外部作用于(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包Python中的闭包原文出处:田小计划(http://www.cnblogs.com/wilber2013/p/4658894.html)闭包(closure)是函数式编程的重要的语法结构
Stella981 Stella981
3年前
Jira 使用手册
<tablestyle"width:100%;margin:200px0300px0;"<tr<thDate</th<thRevisionversion</th<thDescription</th<thauthor</th</tr<tr<td20180614</td<tdV1.0.0</td
Wesley13 Wesley13
3年前
2020软件工程作业03
<styletable{width:100%;/\表格宽度\/margin:auto;/\外边距\/emptycells:show;/\单元格无内容依旧绘制边框\/fontsize:18px;}table,th,td{border:2pxsolidpink;}li{fontsize:1
Stella981 Stella981
3年前
JavaScript函数——闭包
闭包概念只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁例子functionouter(){varlocalVal30;returnlocalVal;}
Wesley13 Wesley13
3年前
go 学习笔记之10 分钟简要理解 go 语言闭包技术
闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍Go语言中什么是闭包以及怎么理解闭包.如果读者对于Go语言的闭包还不是特别清楚的话,可以参考上一篇文章go学习笔记之仅仅需要一个示例就能讲清楚什么闭包(https://www.oschina.net/ac
Wesley13 Wesley13
3年前
Java 日期与时间
Java的日期Java没有内置的日期类,但可以导入java.time包,这个包中包含了许多类,可用于处理日期和时间。例如:<table<tbody<tr<thstyle"width:25%"Java类</th<thstyle"width:75%"描述</th</tr<tr<td<code
吴押狱 吴押狱
1年前
测试用
Inrecentyears,theOscarshavebeenwidelycriticized.Frombeingtoopoliticallycorrecttolackinginnovation,andwithplummetingviewership,th