闭包介绍

闭包就是一个函数与其相关的引用环境组合的一个整体

代码示例:

package main

import "fmt"

func AddUper() func (int) int {
var n int = 20
return func(x int) int {
n = n + x
return n
}
}

func main() {
f := AddUper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}

Golang的闭包和函数 defer介绍_匿名函数

以上代码的说明

1,AddUpper 是一个函数,返回的数据类型是 func(int) int

2,闭包返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包

3,闭包是类,函数是操作,n是字体,函数和它使用到n构成闭包

4,当我们反复调用f函数时,因为n的初始化一次,因此每调用一次就进行累计

5,闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和他引用到的变量共同构成闭包

6,修改之前代码,看示例:

package main

import "fmt"

func AddUper() func (int) int {
var n int = 20
var str = "shanghai"
return func(x int) int {
n = n + x
str += string(36)
fmt.Println("str=",str)
return n
}
}

func main() {
f := AddUper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}

输出结果:

Golang的闭包和函数 defer介绍_匿名函数_02


闭包的最佳实践

1,写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(如:.jpg) 并返回一个闭包

2,调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如:.jpg),则返回文件名.jpg, 如果已有.jpg后缀,返回原文件名

3,要求使用闭包方式完成

4,string.HasSuffix,该函数可以判断某个字符串是否有指定的后缀

package main

import (
"fmt"
"strings"
)

func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}

func main() {
f2 := makeSuffix(".jps")
fmt.Println("文件名处理后=",f2("hi"))
fmt.Println("文件名处理后=",f2("hello"))
}

Golang的闭包和函数 defer介绍_代码示例_03

以上代码的说明

1,返回的匿名函数和makeSuffix(suffix string) 的 suffix变量组合成一个装饰,返回的函数引用到suffix这个变量

2,闭包的好处,如果使用传统的方法,也可以实现这个功能,但是传统方法需要每次都传入后缀名,比如.jpg,而闭包因为可以保留上次引用的某个值 ,所以我们传入一次就可以反复使用。


函数的defer

defer基本介绍

在函数中,程序员经常需要创建资源(如:数据库连接,文件句柄,锁等),为了在函数执行完毕后,及时释放资源,Go提供了defer(延时机制)

代码示例:

package main

import "fmt"

func sum(n1 int, n2 int) int {
defer fmt.Println("shanghai=",n1)
defer fmt.Println("bejing=",n2)

res :=n1 + n2
fmt.Println("hello res=",res)
return res
}
func main() {
res := sum(5, 6)
fmt.Println("res=", res)
}

Golang的闭包和函数 defer介绍_后缀_04


defer的注意事项

1,当go执行到一个defer时,不会立即执行defer后的语句,而是将defer 语句压入一个栈中,然后继续执行函数的下一个语句

2,当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(遵守栈先入后出机制)

3,在defer将语句放入到栈中,也会将相关值拷贝同时入栈

package main

import "fmt"

func sum(n1 int, n2 int) int {
defer fmt.Println("shanghai=",n1)
defer fmt.Println("bejing=",n2)

n1++
n2++
res :=n1 + n2
fmt.Println("hello res=",res)
return res
}
func main() {
res := sum(5, 6)
fmt.Println("res=", res)
}

Golang的闭包和函数 defer介绍_后缀_05


defer的最佳实践

1,defer 最主要的价值在于,当函数执行完毕后,可以及时的释放函数创建的资源 

2,在Golang编程中通常的做法是,创建资源后,如(打开文件,获取数据库的链接或者是锁资源),可以执行defer file.Close(), defer connect.Close()

3, 在defer 后,可以继续使用创建资源

4,当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源

5,这种机制,非常简洁,程序员不用在什么时候关闭资源的操心