Go基础-闭包

什么是闭包?

闭包的应该都听过,但到底什么是闭包呢?

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

"官方"的解释是:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

维基百科讲,闭包(Closure),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

看着上面的描述,会发现闭包和匿名函数似乎有些像。可是可能还是有些云里雾里的。因为跳过闭包的创建过程直接理解闭包的定义是非常困难的。目前在JavaScript、Go、PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、Ruby、 Python、Lua、objective c、Swift 以及Java8以上等语言中都能找到对闭包不同程度的支持。通过支持闭包的语法可以发现一个特点,他们都有垃圾回收(GC)机制。 javascript应该是普及度比较高的编程语言了,通过这个来举例应该好理解写。看下面的代码,只要关注script里方法的定义和调用就可以了。

闭包如何实现

我们实现一个延迟计算总和的函数

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	//add := awaitAdd(2)
	t1 := time.Now()
	sum := awaitAdd(2)(1, 2, 3)
	//sum := add(1, 2, 3)
	since := time.Since(t1)
	fmt.Println(sum, since)
}

func awaitAdd(await int) func(...int) int {
	// 延时函数放在上面和放在下面是有区别的
	fmt.Println("先执行await add")
	time.Sleep(time.Duration(await) * time.Second)
	return func(num ...int) int {
		//time.Sleep(time.Duration(await) * time.Second)
		var sum int
		for _, v := range num {
			sum += v
		}
		return sum
	}
}

从上面的代码可以看出,闭包就是是在函数里面套一个函数,并且再把这个函数作为返回值返回。但是闭包可以被传递给其他函数,作为函数调用的参数;闭包可以被函数返回,作为函数的返回值。关键是作为外部函数,它可以引用函数参数局部变量。

小问

这两个延迟有没有区别?

第一种测试

在匿名函数里延迟

go 复制代码
func awaitAdd(await int) func(...int) int {
	// 延时函数放在上面和放在下面是有区别的
	fmt.Println("先执行await add")
	//time.Sleep(time.Duration(await) * time.Second)
	return func(num ...int) int {
		time.Sleep(time.Duration(await) * time.Second)
		var sum int
		for _, v := range num {
			sum += v
		}
		return sum
	}
}

主函数调用

go 复制代码
func main() {
	add := awaitAdd(2)
	t1 := time.Now()
	//sum := awaitAdd(2)(1, 2, 3)
	sum := add(1, 2, 3)
	since := time.Since(t1)
	fmt.Println(sum, since)
}

打印结果:

powershell 复制代码
先执行await add
6 2.0005028s

第二种测试

go 复制代码
func awaitAdd(await int) func(...int) int {
	// 延时函数放在上面和放在下面是有区别的
	fmt.Println("先执行await add")
	time.Sleep(time.Duration(await) * time.Second)
	return func(num ...int) int {
		//time.Sleep(time.Duration(await) * time.Second)
		var sum int
		for _, v := range num {
			sum += v
		}
		return sum
	}
}

主函数不变,看打印结果:

powershell 复制代码
先执行await add
6 0s

总结

关于闭包我一直都是一知半解,哪怕在此刻也还是有点模糊。但是从这里能看出来,闭包的本身就是函数调内部函数,我这里记录的就是闭包在go里面怎么实现怎么调用。本质上说,在匿名函数里使用了外部的变量,才叫闭包

相关推荐
噢,我明白了1 小时前
JAVA中的一些基础应用知识点
java·开发语言
时间的拾荒人2 小时前
Redis 入门详解:核心特性、重要文件与全局命令实战
开发语言·redis·面试
木头左2 小时前
【架构设计】教育类 App 服务端化改造:AI 上移、题库分层与个人题库归属
android·架构设计·系统设计·后端开发·数据库设计
敲上瘾2 小时前
Qt Widgets入门:项目模板解析与Hello World
开发语言·数据库·c++·qt·ui·qt6.3
墨狂之逸才3 小时前
USB 的 HOST 和 DEVICE 到底有什么区别?ADB 调试应该选哪个?
android
码途行者3 小时前
网站的SQL 注入:从攻击原理到防御措施
开发语言·sql·php
利威尔xu3 小时前
Go map 底层崩溃:3 个 goroutine 并发写同一张 map,fatal error 崩掉整个服务
golang·源码·map·并发·后端开发
奶油话梅糖4 小时前
苹果的内核跑上了安卓手机:2,631 Star 的 HakiOS 让 XNU 在 Adreno GPU 上画出 118 帧
android·macos·智能手机
旖旎夜光4 小时前
【C++ 基础入门】C++ 多态(中):虚函数表、多态原理与动静绑定
开发语言·c++·笔记·学习
恋猫de小郭4 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter