Go语言中的闭包
一、函数的变量作用域和可见性
1.全局变量在main函数执行之前初始化,全局可见
2.局部变量在函数内部或者if、for等语句块有效,使用之后外部不可见
3.全局变量和局部变量同名的情况下,局部变量生效。
4.可见性:
包内任何变量或函数都是能访问的。
包外的话,首字母大写是可以访问的,首字母小写的表示私有的不能被外部调用。
二、匿名函数
1.Go语言中函数也是一种类型,所以可以用一个函数类型的变量进行接收。
func anonyTest1(){
fmt.Println("anonyTest1")
}
//将改函数赋值给一个变量f,执行f
func AnonyTest(){
f:= anonyTest1
f()
}
2.匿名函数就是不指定名称的函数,如下就是匿名函数的使用
func AnonyTest2(){
f:= func() {
fmt.Println("AnonyTest2")
}
f()
//或者
func() {
fmt.Println("AnonyTest2...")
}()
}
3.下面一个例子结合defer来看一下,这三个输出都是什么
func AnonyTest3(){
var i=0
defer func() {
fmt.Printf("defer func i=%v \n",i)
}()
defer fmt.Printf("defer i=%v \n",i)
for;i<10; i++{
}
fmt.Printf("i=%v \n",i)
}
从defer那篇文章我们知道 defer fmt.Printf("defer i=%v \n",i) 打印的就是i初始化后的值,最后一个也一定是for循环之后的值10,
主要就是匿名函数执行之后的值,有意思是10,说明访问了匿名函数外部的i,这就涉及到了闭包
运行结果如下:
i=10
defer i=0
defer func i=10
4.既然函数也是一种类型,那么就可以把函数当做参数进行输入、输出了。(感觉有点类似C#里面的委托)
func Calc(a,b int, op func(int,int)int) int {
return op(a,b)
}
func add(a,b int) int{
return a+b
}
func sub(a,b int)int{
return a-b
}
func AnonyTest4(){
var a = 2
var b = 1
var x = Calc(a,b,add)
var y = Calc(a,b,sub)
fmt.Printf("x=%v, y=%v \n",x,y)
}
结果:
x=3, y=1
三、闭包
闭包是由函数和与其相关的引用环境组合而成的实体(好抽象,难理解啊)
func Adder() func(int) int{
var x int
return func(d int) int{
x+=d
return x
}
}
像上面这段代码,我们可以看到定义了一个变量x,以及return中的匿名函数。我们可以看到匿名函数引用了外部的变量x,我们可以把这个x叫做自由变量。
换句话说,这个匿名函数和这个自由变量x组成了一个整体,只要是在这个整体的生命周期内这个x都是有效的。
下面使用一下这个Adder函数:
func ClosureDemo5(){
var f = Adder()
fmt.Printf("结果=%d\n",f(1))
fmt.Printf("结果=%d\n",f(20))
fmt.Printf("结果=%d\n",f(300))
}
执行结果
结果=1 结果=21 结果=321
正如上面所提到的,这个只要Addr() 也就是f这个对象没有消亡,那么f中的这个x就始终存在,也就是为什么第二次是21,第三次是321的原因了。
其他例子:
例子1:
func Adder2(base int) func(int)int{
return func(i int) int{
base += i
return base
}
}
func main(){
tmp1 := Adder2(10)
fmt.Println(tmp1(1),tmp1(2))
tmp2 := Adder2(100)
fmt.Println(tmp2(10),tmp2(20))
}
这里Adder2接收一个int类型参数base,然后返回一个func,这里这个匿名函数里面引用了这个参数base,那么这个参数base和匿名函数就形成了一个整体。
后面我们 tmp1被赋值为 Adder2(10) ,那么在tmp1这个对象的生命周期内,base是被初始化为10且一直存在,所以结果是 11 和 13,同理后面是 110 和 130
例子2:
func calc(base int) (func(int)int,func(int)int){
add:= func(i int)int{
base +=i
return base
}
sub:= func(i int)int{
base -= i
return base
}
return add,sub
}
func main(){
f1,f2 := calc(10)
fmt.Println(f1(1),f2(2))
fmt.Println(f1(3),f2(4))
fmt.Println(f1(5),f2(6))
fmt.Println(f1(7),f2(8))
}
分析一下:
这里base和 add以及sub的匿名函数也组成了一个实体也就是calc,所以在f1和f2的生命周期内,base一直存在,并被初始化成了10.
所以结果就是 f1(1) 就是10+1 =11 而 f2(2)就是 11-2 = 9,其他同理。
所以结果如下:
闭包的副作用!
func main(){
for i:=0;i<5;i++{
go func(x int){
fmt.Println(x)
}(i)
}
time.Sleep(time.Second)
}
上述代码应该结果是多少?我的猜想应该是0、1、2、3、4
但是实际结果是:
5
5
5
5
5
为什么会出现这样的情况?实际上面这里每一个go协程中的匿名函数和外部for循环的i也形成了闭包,因为for循环执行比较快,所以go还没来得及执行就变成5了。
我在每一个go协程之后加一个延时,结果就是0,1,2,3,4了。
func main(){
for i:=0;i<5;i++{
go func(){
fmt.Println(i)
}()
time.Sleep(time.Second)
}
time.Sleep(time.Second)
}
结果如下
0
1
2
3
4
问题就在于不可能每次执行都进行延迟吧,所以需要做一件事情打破这个闭包。
func main(){
for i:=;i<;i++{
go func(x int){
fmt.Println(x)
}(i)
}
time.Sleep(time.Second)
}
这里把i当做参数传入到匿名函数中,保证了每次循环传的值都不一样。
Go语言中的闭包的更多相关文章
- 001_解析go语言中的闭包
go语言中的闭包,是大家学习go语言的一个大难点,笔者在学习时候也是痛苦不堪,在来回对比了其它语言的用法,并且查阅了很多网上的文章,终于对闭包有了一个较为清晰的认识,以下就是关于闭包的解析 首先看一个 ...
- 015_go语言中的闭包
代码演示 package main import "fmt" func intSeq() func() int { i := 0 return func() int { i++ r ...
- [译] Closures in Lua - Lua中的闭包
原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...
- [转][译] Closures in Lua - Lua中的闭包
http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...
- javaScript中的闭包原理 (译)
这篇文章通过javaScript代码解释了闭包的原理,来让编程人员理解闭包.它不是写给大牛或使用功能性语言进行编程的程序员的.一旦意会了其核心概念,闭包理解起来并不难.然而,你不可能通过阅读任何有关闭 ...
- 彻底弄懂js循环中的闭包问题
来源:http://www.108js.com/article/article1/10177.html?id=899 第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然 ...
- javascript中的闭包解析
学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去 ...
- 如何在C语言中调用Swift函数
在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中 ...
- js中的闭包之我理解
闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的 ...
随机推荐
- docker-compose使用备忘(转)
Docker-Compose简介 Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排. Docker-Compose将所管理的容器分为三层,分别是工 ...
- 【译】.NET Core 3.0 Preview 3中关于ASP.NET Core的更新内容
.NET Core 3.0 Preview 3已经推出,它包含了一系列关于ASP.NET Core的新的更新. 下面是该预览版的更新列表: Razor组件改进: 单项目模板 新的Razer扩展 E ...
- 表单数据验证方法(二)——ASP.NET后台验证
昨天写了一下关于如何在前台快捷实现表单数据验证的方法,今天接着昨天的,把后台实现数据验证的方法记录一下.先说明一下哈,我用的是asp.net,所以后台验证方法也是基于.net mvc来做的. 好了,闲 ...
- 分享几个常见的CMD命令,可能会用的上
win7快捷命令.CMD命令secpol.msc(设置开机启动提示信息)services.msc(打开服务)dxdiag(检查DirectX信息)winver(检查Windows版本)regedit( ...
- Identity4实现服务端+api资源控制+客户端请求
准备写一些关于Identity4相关的东西,最近也比较对这方面感兴趣.所有做个开篇笔记记录一下,以便督促自己下一个技术方案方向 已经写好的入门级别Identity4的服务+api资源访问控制和简单的客 ...
- kali linux 2019.1 替换burpsuite pro 1.7.37
下面全部实操: 先切换JDK版本为1.8.执行 update-alternatives --config java 然后会显示多个jdk版本,1.8选3 输入java –version会显示jdk为1 ...
- 【设计模式】建造者模式 Builder Pattern
前面学习了简单工厂模式,工厂方法模式以及抽象工厂模式,这些都是创建类的对象所使用的一些常用的方法和套路, 那么如果我们创建一个很复杂的对象可上面的三种方法都不太适合,那么“专业的事交给专业人去做”,2 ...
- K3日志定时备份
K3日志超过5万条以后,每次用户登陆后,系统都会提示日志太多.但是日志又不能随意删除,所以需要做个数据库定时任务,定时把日志转移到备份表. declare @dt datetime;; SELECT ...
- Java三大特性
Java 三大特性,算是Java独特的表现,提到Java 的三大特性, 我们都会想到封装, 继承和多态 这是我们Java 最重要的特性. 封装(Encapsulation) : 封装:是指隐藏对象的属 ...
- iOS 多线程 NSOperation、NSOperationQueue
1. NSOperation.NSOperationQueue 简介 NSOperation.NSOperationQueue 是苹果提供给我们的一套多线程解决方案.实际上 NSOperation.N ...