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能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的 ...
随机推荐
- AngularJs with Webpackv1 升級到 Webpack4
本篇記錄一下升級的血淚過程 請注意升級前請先創一個新目錄將升級應用與舊應用隔離 1. 需要將相關的套件做統一升級的動作,已確認需要升級所有舊的loaders 其它應用的套件可先不做升級的動作 (如果編 ...
- .NET Core和.NET Standard有什么不同
近日,微软发布了.NET Core 2.0,但是开发人员中间仍然存在一些疑惑,就是.NET Core..NET Standard.Xamarin和.NET Framework有什么不同. .N ...
- linux服务器运维管理学习
一. 了解linux 1.Linux操作系统是基于UNIX操作系统发展而来的一种克隆系统,它诞生于1991 年的 [Linux桌面] 10 月5 日(这是第一次正式向外公布的时间).以后借助于Inte ...
- 关于jQuery中的选择器
1:选择器的作用 获取网页的上面的标签元素等等,然后对他进行一些列的操作(添加样式,添加行为...) 2:选择器有哪些 基本选择器,层次选择器,过滤选择器,表单选择器 一:基本选择器 基本选择器是jq ...
- asp.net mvc前台显示带htm标签的解决办法(Razor —@Html.Raw())
数据是从后台富文本编辑后丢在数据库后取出的,不加Html.Raw(),前台就会把Html标签一同显示
- Netty解决粘包和拆包问题的四种方案
在RPC框架中,粘包和拆包问题是必须解决一个问题,因为RPC框架中,各个微服务相互之间都是维系了一个TCP长连接,比如dubbo就是一个全双工的长连接.由于微服务往对方发送信息的时候,所有的请求都是使 ...
- Spring MVC(一)五大核心组件和配置
一,五大核心组件 1.DispatcherServlet 请求入口 2.HandlerMapping 请求派发,负责请求和控制器建立一一对应的关系 3.Controller 处理器 4.Mod ...
- html和css的使用方法以及样式
布局步骤 第一步: 清除默认样式第二步: 划分模块第三步: 设置模块的大小以及位置第四步: 划分下一级模块 html和css 引入网页头像 <link rel="shortcut ic ...
- 通用HttpClientUtil工具类
package com.*.utils; import java.io.IOException; import java.net.URI; import java.util.ArrayList; im ...
- Delphi IfThen语句
function IfThen(AValue: Boolean; const ATrue: string; AFalse: string = ''): string; overload; $[StrU ...