持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
一、闭包:
一、介绍:
基本介绍:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
闭包:函数内部引用函数外的变量就是闭包
二、案例演示:
package main
import "fmt"
// 累加器
func AddUpper() func(int) int {
var n int = 10
return func(x int) int {
n = n + x
return n
}
}
func main() {
// 使用前面的代码
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
对上面代码的说明和总结:
- AddUpper() ,是一个函数,返回的数据类型是 func (int) int
- 闭包的说明:返回的是一个匿名函数,但是这个匿名函数引用到函数外的 n ,因此这个匿名函数就和 n 形成一个引用整体,构成闭包。
// 累加器
func AddUpper() func(int) int {
var n int = 10
return func(x int) int {
n = n + x
return n
}
}
- 大家可以这样理解:闭包是一个类,函数是操作,n 是字段(就是把这个闭包看成一个类,然后底下把这个类赋值给一个变量。相当于新建一个对象,但是对象不变,所以类里面的数值就是一直用的)
- 当我们反复调用f函数时,因为n是初始化一次,因此每调用一次就进行累加。(AddUpper 的返回值赋给了 f ,所以 f 是一个匿名函数,而n 是一个函数外的变量,可以当作是全局变量,每次加完之后 n 是不会重置的)
- 我们要搞清楚闭包的关键,就是要分析出返回的函数,它使用(引用)到了哪些变量,因为函数和其引用的变量构成了闭包。
package main
import "fmt"
// 累加器
func AddUpper() func(int) int {
var n int = 10
var str = "hello"
return func(x int) int {
n = n + x
str += string(36) // ==> 36 = '$'
fmt.Println("str=", str)
return n
}
}
func main() {
// 使用前面的代码
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
三、闭包的最佳实践:
请编写一个程序,具体要求如下:
- 编写一个函数
makeSuffix(suffix string)可以接收一个文件后缀名(比如.jpg),并返回一个闭包。 - 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回原文件名。
- 要求使用闭包的方式完成。
string.HasSuffix,该函数可以判断某个字符串是否有指定的后缀。
package main
import (
"fmt"
"strings"
)
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
// 如果 name 没有指定的后缀,则加上,否则就返回原来的名字。
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
// 测试一下makeSuffix使用
// 返回一个闭包
f := makeSuffix(".jpg")
fmt.Println("文件名处理后=", f("winter"))
fmt.Println("文件名处理后=", f("bird.jpg"))
}
对上面代码的说明和总结:
- 返回的函数和
makeSuffix(suffix string)的suffix变量和返回的函数组合成一个闭包,因为,返回的函数引用到suffix这个变量。 - 我们需要仔细体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入后缀名,比如
.jpg,而闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复使用,大家可以仔细体会一下。
package main
import (
"fmt"
"strings"
)
// 使用闭包的方式:
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
// 如果 name 没有指定的后缀,则加上,否则就返回原来的名字。
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
// 不使用闭包的方式:
func makeSuffix2(suffix string, name string) string {
// 如果 name 没有指定的后缀,则加上,否则就返回原来的名字。
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
func main() {
// 测试一下makeSuffix使用
// 返回一个闭包
// 如果使用闭包,好处是只需要传入一次后缀。
f := makeSuffix(".jpg")
fmt.Println("文件名处理后=", f("winter"))
fmt.Println("文件名处理后=", f("bird.jpg"))
// 不使用闭包
fmt.Println("文件名处理后=", makeSuffix2("jpg", "winter"))
fmt.Println("文件名处理后=", makeSuffix2("jpg", "bird.jpg"))
}