我是谁

defer - 顾名思义翻译过来叫 延迟, 所以我们通常称呼 defer func() 这样 defer 后面紧跟的函数为 延迟函数.

作者注: 不过从实际应用来讲, 延迟函数通常用来做一些函数最终返回前的一些收尾工作, 所以称呼为收尾函数其实更贴切.

三围几何

defer 有其独特的一面, 了解其个性, 才能更好的下手...对待妹纸, 应该也是这么个理

延迟性

顾名思义, 既然叫延迟函数, 那么肯定具备延迟性.

我们来看看怎么个延迟法, defer_defer.go

// defer_defer.go
package main import (
"fmt"
) func main() {
foo()
} func foo() {
fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
} // go run defer_defer.go
// 1
// 3
// 2

可以看到 defer 定义的延迟函数最后才执行.

再来个例子, 如果一个函数内出现多个延迟函数, 延迟函数的执行顺序又是怎么样的呢?

defer_filo.go

// defer_filo.go
package main import (
"fmt"
) func main() {
foo()
} func foo() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
} // go run defer_filo.go
// 3
// 2
// 1

可以看到先定义的延迟函数后执行, 后定义的延迟函数先执行, 符合栈 (stack) 的先进后出 (FILO) 原则.

影响性

直接看代码, defer_impact.go

// defer_impact.go
package main import (
"fmt"
) func main() {
fmt.Println(foo())
} func foo() (result int) {
defer func() {
result++
}()
return 0
} // go run defer_defer.go
// 1

结果是不是跟想象有点不一样? 上述 foo() 可以改写为下:

func foo() (result int) {
result = 0
result++
return
}

go 中的 return 语句不是原子操作.

go 中 return 语句的操作过程为:

  • 设置返回值
  • 执行延迟函数
  • 真正 return

所以延迟函数会影响主函数的返回值, 当然还要区分具名返回值/匿名返回值, 后话再说.

确定性

延迟函数的参数值, 在延迟函数首次出现时就确定了, 不受后续操作的影响.

我们来个例子: defer_parameters.go

// defer_parameters.go
package main import (
"fmt"
) func main() {
foo()
} func foo() {
number := 1
defer fmt.Println(number)
number = 2
return
} // go run defer_parameters.go
// 1

能做啥

  • 打开数据链接, 要记得关闭, 可以用 defer
  • 操作完内存资源, 要记得释放, 可以用 defer
  • 想改变主函数的具名返回值, 可以用 defer - 通常不会这么干
  • 想奇淫技巧, 可以用 defer - 偶尔 show 偶尔爽, 一直 show 一直爽
  • 想搞事情, 可以用 defer - 请自行确保生命和财产安全
  • ...

参考:

go 关键字之 defer的更多相关文章

  1. Go语言 关键字:defer

    defer和go一样都是Go语言提供的关键字.defer用于资源的释放,会在函数返回之前进行调用.一般采用如下模式: f,err := os.Open(filename) if err != nil ...

  2. go语言defer关键字背后的实现,语法,用法

    原文: https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html ------------------------------ ...

  3. go 学习笔记之咬文嚼字带你弄清楚 defer 延迟函数

    温故知新不忘延迟基础 A "defer" statement invokes a function whose execution is deferred to the momen ...

  4. golang中defer的正确使用方式(源自深入解析go)

    3.4 defer关键字 defer和go一样都是Go语言提供的关键字.defer用于资源的释放,会在函数返回之前进行调用.一般采用如下模式: f,err := os.Open(filename) i ...

  5. Javascript是单线程的深入分析

    本来想总结一下的,网上却发现有人已经解释的很清楚了,特转过来. 这也解释了为什么在用自动化测试工具来运行dumrendtree时设定的超时和测试case设定的超时的关联性. 面试的时候发现99%的童鞋 ...

  6. 从C++到GO

    从C++到GO 刚开始接触Go语言,看了两本Go语言的书,从c++开发者的角度来看看go语言的新特性,说下自己感触较深的几点: 并发编程 Go语言层面支持协程,将并发业务逻辑从异步转为同步,大幅提高开 ...

  7. 深入分析 Javascript 单线程

    面试的时候发现99%的童鞋不理解为什么JavaScript是单线程的却能让AJAX异步发送和回调请求,还有setTimeout也看起来像是多线程的?还有non-blocking IO, event l ...

  8. go语法之一

    Go语法: Go语言要求public的变量必须以 大写字母开头,private变量则以小写字母开头,这种做法不仅免除了public.private关键字,更重要的是统一了命名风格. Go语言对{  } ...

  9. 掌握一门语言Go

    摘要:Go语言的优势不必多说,通过本篇文章,让我们花时间来掌握一门外语,Let's Go! 关键字:Go语言,闭包,基本语法,函数与方法,指针,slice,defer,channel,goroutin ...

随机推荐

  1. luogu P1352 没有上司的舞会 x

    P1352 没有上司的舞会 题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员 ...

  2. WTL拖拽文件

    1.对话框属性中把:Accept Files设置为TRUE,或者ModifyStyleEx(0, WS_EX_ACCEPTFILES); 2.在对话框中添加一个ListBox控件,并关联成员变量:m_ ...

  3. Vue(核心思想)

    1.Es6语法普及 let和var的区别: var:定义变量时,在全局范围内都有效;所以在变量没有声明之前就能使用,值为undefined, 称为变量提升; let:声明的变量一定要在声明后使用,而且 ...

  4. oracle 数据库优化【转载】

    版权声明:本文为CSDN博主「咫尺的梦想ing」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/u01305 ...

  5. Yii2.0简单隐藏index.php文件和模块配置和layout布局配置禁用和日志写入配置

    隐藏index.php文件 目的:想去掉浏览器地址栏中的 index.php?r= 这一块. 在/config/web.php中   ’components'=>[]   中添加如下代码: 'u ...

  6. SRCNN 卷积神经网络

    2019-05-19 从GitHub下载了代码(这里) 代码量虽然不多,但是第一次学,花了时间还是挺多的.根据代码有跑出结果(基本没有改),但是对于数据集的处理还是看的很懵逼,主要是作者的实现都是用类 ...

  7. leetcode-easy-array-31 three sum

    mycode  69.20% class Solution(object): def removeDuplicates(self, nums): """ :type nu ...

  8. 无法加载程序集XXX.dll 此程序集可能是从 Web 上下载的

    错误    13    无法加载程序集 file:///D:\Documents\Downloads\kaxaml-master\kaxaml-master\packages\Prism.4.0.0. ...

  9. java网络通信:netty

    Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一个基于NIO的客户,服务器端编程框架,使用Netty 可以确保 ...

  10. 用例a失败,跳过测试用例b和c并标记失败xfail

    前言 当用例a失败的时候,如果用例b和用例c都是依赖于第一个用例的结果,那可以直接跳过用例b和c的测试,直接给他标记失败xfail用到的场景,登录是第一个用例,登录之后的操作b是第二个用例,登录之后操 ...