关于什么是闭包,我相信大家初次听到这个概念的时候一定是非常迷茫,我也是查找了很多资料也还是没有那么透彻的理解,但是在之后的实践中通过逐渐的使用好像更了解了闭包是什么,所以希望通过此篇文章来和大家分享一下我对闭包的理解。
1. 什么是闭包
我认为的闭包就是一个函数与这个函数外部变量的一个封装。粗略的可以理解为一个类,类里面有变量和方法,其中闭包所包含的外部变量对应着类中的静态变量。 为什么这么理解,首先让我们来看一个例子。
func add() func(int) int {
n := 10
str := "string"
return func(x int) int {
n = n + x
str += strconv.Itoa(x)
fmt.Println(str)
return n
}
}
func main() {
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
这个程序的输出结果是
string1
11
string12
13
string123
16
如果不了解的闭包的朋友肯定会觉得很奇怪,为什么会输出这样的结果。这就要用到我最开始的解释。闭包就是一个函数和一个函数外的变量的封装,而且这个变量就对应着类中的静态变量。 这样就可以将这个程序的输出结果解释的通了。
- 最开始我们先声明一个函数
add
,在函数体内声明一个闭包
- 其中的
n
,str
与下面的匿名函数构成了整个的闭包,n
与str
就像类中的静态变量只会初始化一次,所以说尽管后面多次调用这个闭包,会面都不会再重新初始化了 - 而且对于外部变量的操作是累加的,这与类中的静态变量也是一致的
通过以上的解释我们再来理解一下开头的那个程序,就能解释的通了。
2. 闭包的好处
对于一个函数来说,如果不调用闭包也能实现绝大部分功能,但是为什么要使用闭包呢,我认为有以下原因
- 通过封装的方式使代码更简洁,减少全局变量的使用
- 通过闭包我们可以减少参数的传递
我们可以通过这个例子来对比一下,这个函数是判断一个字符串的后缀是否为.jpg
,如果不是的话,则在字符串的末尾加上.jpg
,否则返回原值,我们分别使用闭包和普通函数实现以下,我们来看一下区别。
//闭包实现
func makeSuffix(suffix string) func (string) string{
return func (name string) string{
if !strings.HasSuffix(name, suffix){
return name + suffix
}
return name
}
}
//普通函数实现
func makeSuffix2(name , suffix string) string {
if !strings.HasSuffix(name, suffix){
return name + suffix
}
return name
}
func main() {
f := makeSuffix(".jpg")
fmt.Println(f("winter"))
fmt.Println(f("hello.jpg"))
fmt.Println(makeSuffix2("winter", ".jpg"))
fmt.Println(makeSuffix2("hello.jpg", ".jpg"))
}
通过这个例子我想大家能够更好的理解闭包的好处了。
3. 总结
总的来说,在没有类这种概念的情况下,golang通过使用闭包的概念仍然可以实现一些简单的封装,使得代码更加简洁,使用起来也更加方便,但是闭包可能会存在占用内存等问题,所以在使用的过程中也要注意。
我也找到了js中闭包的一个优秀解答链接在这里