【第二十一篇】GO 闭包🔥🔥

109 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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))
}

对上面代码的说明和总结:

  1. AddUpper() ,是一个函数,返回的数据类型是 func (int) int
  2. 闭包的说明:返回的是一个匿名函数,但是这个匿名函数引用到函数外的 n ,因此这个匿名函数就和 n 形成一个引用整体,构成闭包。
// 累加器
func AddUpper() func(int) int {
	var n int = 10
	return func(x int) int {
		n = n + x
		return n
	}
}
  1. 大家可以这样理解:闭包是一个类,函数是操作,n 是字段(就是把这个闭包看成一个类,然后底下把这个类赋值给一个变量。相当于新建一个对象,但是对象不变,所以类里面的数值就是一直用的)
  2. 当我们反复调用f函数时,因为n是初始化一次,因此每调用一次就进行累加。(AddUpper 的返回值赋给了 f ,所以 f 是一个匿名函数,而n 是一个函数外的变量,可以当作是全局变量,每次加完之后 n 是不会重置的)
  3. 我们要搞清楚闭包的关键,就是要分析出返回的函数,它使用(引用)到了哪些变量,因为函数和其引用的变量构成了闭包。
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))
}


三、闭包的最佳实践:

请编写一个程序,具体要求如下:

  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 {
		// 如果 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"))
}

对上面代码的说明和总结:

  1. 返回的函数和 makeSuffix(suffix string)suffix 变量和返回的函数组合成一个闭包,因为,返回的函数引用到 suffix 这个变量。
  2. 我们需要仔细体会一下闭包的好处,如果使用传统的方法,也可以轻松实现这个功能,但是传统方法需要每次都传入后缀名,比如.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"))
}