Go函数下篇:defer和闭包

package main

import "fmt"

func work() int {
    num := 10
    defer func(i int) {
        i += 20
        println("defer内的结果:", i)
    }(num)
    return num
}

func main() {
    ret := work()
    fmt.Println(ret)
}

输出:

defer内的结果: 30
10

解析:在work函数内,变量num传递给了通过defer注册了的匿名函数,在匿名函数内做了加20,但它的结果并未影响到外面的num变量,这就是值拷贝。

package main

import (
    "fmt"
    "os"
)

func main() {
    defer func() {
        fmt.Println("释放锁...")
    }()
    fmt.Println("hello")
    os.Exit(1)
}

输出:

hello

答案很明显了,居然不会。

package main

import (
    "fmt"
)

func closeFile() {
    println("关闭文件")
}

func work() bool {
    defer closeFile()
    fmt.Println("打开文件")
    fmt.Println("正在处理...")
    fmt.Println("处理完成!")
    return true
}

func main() {
    status := work()
    fmt.Println(status)
}

输出:

打开文件
正在处理...

处理完成!
true
关闭文件

所以说,defer的应用场景可以是释放资源或者关闭连接、关闭文件、释放锁等等,说白了就是:它就是做收尾工作的家伙。

闭包的本质其实返回的是一个函数,但这个函数有点特殊,为啥说特殊呢?因为在这个函数里面,还有一个函数,这个函数是匿名函数,且在这个匿名函数里面还可以引用外部变量,当被反复调用时,这里引用的外部变量只会初始化一次。因此:闭包=函数+函数里面的匿名函数+匿名函数引用的外部变量

接下来一步一步进行解剖

刚才说到闭包的本质其实返回的是一个函数,之前提到过,匿名函数是可以作为函数的返回值的,看下面代码:

func bibao() func() {

}

上面的栗子中,定义了一个函数Bibao,这个函数没有入参,返回值的类型是函数类型,也就是定义成func(),所以它要返回一个函数才行呢,现在函数体是空的,别急,一步一步解剖。

刚才说到在这个函数里面,还有一个函数,这个函数是匿名函数,且还要返回这个匿名函数,看下面的代码:

func bibao() func() {
    return func() {
        fmt.Println("hello")
    }
}

func main() {
    b := bibao()
    b()
}

输出:

hello

在bibao函数体里,返回了一个匿名函数,这个匿名函数的功能是输出了字符串”hello”,调用bibao()把它赋值给变量b,此刻,变量b就是一个函数(它接收的就是返回的匿名函数),变量b既然是函数,那就可以进行调用了b(),所以执行后,输出的是”hello”。

刚才说到且在这个匿名函数里面还可以引用外部变量,当被反复调用时,这里引用的外部变量只会初始化一次,看下面代码:

package main

import "fmt"

func bibao(name string) func() string {
    base := "hello"
    return func() string {
        base = base + " " + name
        return base
    }
}

func main() {
    b := bibao("ttr")
    fmt.Println(b()) // 第1次调用
    fmt.Println(b()) // 第2次调用
    fmt.Println(b()) // 第3次调用
    fmt.Println(b()) // 第4次调用
}

输出:

hello ttr //第1次调用的结果输出
hello ttr ttr //第2次调用的结果输出
hello ttr ttr ttr //第3次调用的结果输出
hello ttr ttr ttr ttr //第4次调用的结果输出

这次对bibao函数做了点小改造,它需要接收一个参数name。在bibao函数中,对于匿名函数内部来说,它的外部变量就是bibao函数里的base变量。调用函数bibao并传入”ttr”,在匿名函数中进行了字符串拼接,bibao(“ttr”)初始化了一次,b()反复调用了4次,在匿名函数中引用的外部变量base,每次调用时返回的是外部变量base的多个副本,因为在每次调用时都会为局部变量分配内存(对于整个程序来说,在函数里的base变量是局部变量,而对于在bibao函数里的匿名函数来说,base变量是外部变量。)

还可以再这样改造,效果也是一样的:

package main

import "fmt"

func bibao() func(name string) string {
    base := "hello"
    return func(name string) string {
        base = base + " " + name
        return base
    }
}

func main() {
    b := bibao()
    fmt.Println(b("ttr"))
    fmt.Println(b("ttr1"))
    fmt.Println(b("ttr2"))
}

输出:

hello ttr
hello ttr ttr1
hello ttr ttr1 ttr2

好了,到此为止,闭包到底在哪呢?下面这块就是闭包,bibao函数返回了这个闭包:

base := "hello"
return func(name string) string {
    base = base + " " + name
    return base
}

闭包是由函数和与其相关的引用环境组合而成的实体,这里所说的函数就是匿名函数,所说的引用环境就是外部变量base,他们一起组合成了一个实体并返回,这就是闭包。

突然想到如果把base变量放到匿名函数里面,它还是不是一个闭包?看看效果:

package main

import "fmt"

func bibao() func(name string) string {
    return func(name string) string {
        base := "hello "
        base = base + " " + a + " " + name
        return base
    }
}

func main() {
    b := bibao()
    fmt.Println(b("ttr"))
    fmt.Println(b("ttr1"))
    fmt.Println(b("ttr2"))
}

输出:

hello ttr
hello ttr1
hello ttr2

闭包的好处在于,可以减少全局变量,在函数调用的过程中隐式的传递共享变量,这更具有封装性,更安全了,使其不能随意访问到共享变量。

package main

import (
    "fmt"
)

func bankcard(name string) func() (string, float32) {
    amount := 10000.0
    return func() (string, float32) {
        return name, float32(amount)
    }

}

func main() {
    b := bankcard("xiaoming")
    fmt.Println(b())
}

Original: https://www.cnblogs.com/ttropsstack/p/16736121.html
Author: 不背锅运维
Title: Go函数下篇:defer和闭包

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582284/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 2021 个人年度小结

    因为不用考研,所以大四一整年可以自由自在地学习一直以来想学却又没时间去学的东西。快乐的大四时光总是显得十分短暂,这篇博客主要用来总结过去一年所学的知识。 计算机组成原理 上的是哈尔…

    Linux 2023年6月7日
    094
  • Centos 7.x 线上安装 Kubernetes

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 安装依赖包 yum install -y conntrack ntpdate ntp ipvsadm ipset jq ipta…

    Linux 2023年5月27日
    0114
  • Celery + Redis 的探究

    不在乎过程的,可以直接看最后的结论。 测试代码: 先将 redis 部署于本机的 6379 默认端口 不要设置密码,使用 celery 版本 3.1.23 [1]先直接发起一个 t…

    Linux 2023年5月28日
    067
  • http代理连接

    基于Linux服务器的http代理连接 1. 准备工作 目标服务器 &…

    Linux 2023年6月14日
    070
  • 用户管理

    用户组 种类 基本组: 一个用户一定要有一个基本组 ,且只有一个 附加组: 一个用户可以没有附加组,一个用户可以有多个附加组 分别基本组和 附加组?[root@localhost …

    Linux 2023年6月6日
    0129
  • 2021年3月-第03阶段-前端基础-JavaScript基础语法-JavaScript基础第01天

    1 – 编程语言 1.1 编程 编程: 就是让计算&amp…

    Linux 2023年6月8日
    088
  • js学习笔记——条件 循环

    今天发现之前学的爱前端的课中JS部分函数等不全,果断换了一个课——渡一的《Web前端开发JavaScript高薪课堂》接着学习,不过废话有点多 语法:1、单if,条件成立,执行语句…

    Linux 2023年6月13日
    065
  • 离线版centos8环境部署迁移监控操作笔记

    嗨咯,前两天总结记录了离线版centos8下docker的部署笔记,今天正好是2021年的最后一天,今天正好坐在本次出差回家的列车上,车上没有上面事做,索性不如把本次离线版cent…

    Linux 2023年6月14日
    099
  • vscode搜索所有文件夹中所有文件的方法

    最近在看opencv相关的内容,看到画图这一部分时,提示我 这些代码都来自OpenCV代码的sample文件夹。 按照他的提示,我打开了相应的文件夹,却发现,so many 文件 …

    Linux 2023年6月14日
    0262
  • 分布式运算中,高精度校时器的畅想

    这是我写的,带有一定的娱乐性质的文章。你可以把它理解为神经病的yy。昨天,我看了个帖子《Facebook工程师开发开源自计时设备 仅需一个PCIe插槽即可工作》,有感而发写了此文。…

    Linux 2023年6月14日
    088
  • python爬虫爬取国家科技报告服务系统数据,共计30余万条

    python爬虫爬取国家科技报告服务系统数据,共计30余万条 按学科分类【中图分类】 共计三十余万条科技报告数据 爬取的网址:https://www.nstrs.cn/kjbg/n…

    Linux 2023年6月14日
    073
  • 自动化集成:Pipeline整合Docker+K8S

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译、打包、镜像构建、部署等操作; 本篇文章主要描述流水线集成K8S用法。 一、背景…

    Linux 2023年5月27日
    0162
  • Nginx/Tengine安装配置详解

    1 概念 Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。官方测试…

    Linux 2023年5月27日
    0129
  • 画图3D Paint 3D工作区黑屏

    最近不知道画图3D抽什么风,黑屏了。 后来研究很久,发现这货竟然是用独立显卡,集显带不起来。 解决方案是在Nvidia控制面板给他分配独立显卡,不要使用集显,不要使用集显,不要使用…

    Linux 2023年6月13日
    0100
  • rsync

    rsync简介 rsync是linux系统下的数据镜像备份工具。使用快速增量备份工具Remote Sync可以远程同步,支持本地复制,或者与其他SSH、rsync主机同步。 rsy…

    Linux 2023年6月6日
    082
  • Linux 将本地文件上传Linux服务器, 即ssh 命令上传本地文件

    在linux下一般用scp这个命令来通过ssh传输文件。 1、从服务器上下载文件 scp username@servername:/path/filename /var/www/l…

    Linux 2023年6月13日
    0108
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球