go-函数-深入理解闭包

文章目录背景知识声明标准声明格式例子批量声明例子短变量声明匿名函数例子闭包什么是闭包例子1例子2例子3例子4总结背景知识声明先解释一下这个词“声明”就是把一种类型的变量和一个名字联系起来。标准声明格式var 变量名 变量类型例子var name string批量声明例子var ( a string b int c bool d float32)短变量声明短变量声明只能在函数内部使用。package mainimport ( "fmt
摘要由CSDN通过智能技术生成

一、背景知识

1.声明

先解释一下这个词
“声明”就是把一种类型的变量和一个名字联系起来。

1.1标准声明

格式
var 变量名 变量类型
例子
var name string

1.2批量声明

例子
var (
    a string
    b int
    c bool
    d float32
)

1.3短变量声明

短变量声明只能在函数内部使用。

package main

import (
	"fmt"
)
// 全局变量m
var m = 100

func main() {
   
	n := 10
	m := 200 // 此处声明局部变量m
	fmt.Println(m, n)
}

Go语言中的变量必须先声明后,才能使用,并且

  • 同一作用域内不支持重复声明
  • Go语言的变量声明后必须使用。

2.匿名函数

首先函数是go语言的“一等公民”,这就意味着有很多功能,匿名函数就是一种。
匿名函数就是没有函数名的函数。
因为没有函数名,所以只能立即执行的方式使用或者通过赋值给某个变量(包括作为函数入参/返回值),然后通过变量调用。

匿名函数的定义格式如下:

func(参数)(返回值){
   
    函数体
}

例子

package main

import (
	"fmt"
)

var f = func(int) {
   }

func main() {
   
	//像普通的类型(整型、字符串等)一样进行赋值
	f = func(i int) {
   
		fmt.Println(i)
	}
	f(2)
	//f可以被任何输入一个整型,无返回值的函数给赋。
	//因此f可以看成是一个函数类型的变量。这样,可以动态的改变f的功能。
	f = func(i int) {
   
		fmt.Println(i * i * i)
	}
	f(2)
	// 匿名函数作为函数入参
	testFuncFunc(func(in int) {
   
		fmt.Println(in)
	})

	// 函数作为返回值
	f = inputFunc()
	f(4)

	//函数自执行
	func(in int){
   
		fmt.Println(in)
	}(5)

}
func testFuncFunc(f func(int))  {
   
	f(3)
}

func inputFunc() func(in int){
   
	// 由于函数内部不能定义函数,只能定义匿名函数
	return func(in int) {
   
		fmt.Println(in)
	}
}

/*
2
8
3
4
5
*/

二、闭包

2.1什么是闭包

Go中的闭包指:一个函数和与其相关的引用环境组合而成的实体。
说实话这句话真的难理解,其实改成下面这样就好理解了
闭包是匿名函数与匿名函数所引用环境的组合。

匿名函数有动态创建的特性,该特性使得匿名函数不用通过参数传递的方式,就可以直接引用外部的变量。这就类似于常规函数直接使用全局变量一样,个人理解为:匿名函数和它引用的变量以及环境,类似常规函数引用全局变量处于一个包的环境。
看下面的例子更好理解

package main

import "fmt"

func main() {
   
	n := 0
	f := func() int {
   
		n += 1
		return n
	}
	fmt.Printf("%p\n", f)//这是个地址,结果可能不一样
	fmt.Println(f())
	fmt.Println(f())
}
/*
输出:
0x10a4ae0
1
2
*/

在上述代码中,

n := 0
f := func() int {
   
	n += 1
	return n
}

就是一个闭包
其中

f := func() int {
   
	n += 1
	return n
}

是匿名函数
在匿名函数中引用的变量n就可以看作这个匿名函数引用的环境
再仔细分析一下上面的例子
类比于常规函数+全局变量+包。f不仅仅是存储了一个函数的返回值,它同时存储了一个闭包的状态。这里虽然两次调用f(),但是只初始化了一次,即环境都是一样的

再看一个闭包作为返回值的例子

package main

import "fmt"

func main() {
   
	add := func(base int) func(int) int {
   
		return func(i int) int {
   
			return base + i
		}
	}
	add5 := add(5)
	fmt.Println("add5(10)=", add5(10))
	fmt.Println("add5(15)=", add5(15))
}
/*
结果
add5(10)= 15
add5(15)= 20
 */

这个例子中的“匿名函数”指返回的函数,“引用环境”指其周围的环境,比如base变量等

add是一个闭包作坊(但是大家都说成add是一个闭包),根据入参返回(生产)一个闭包。这样add5就是使用5作为add的参数得到的一个闭包。

闭包被返回赋予一个同类型的变量时,同时赋值的是整个闭包的状态,该状态会一直存在外部被赋值的变量add5中,直到add5被销毁,整个闭包也被销毁。

再看这个闭包例子,内部的匿名函数“捕获”了和它在同一作用于的其他常量和变量(base变量)。这里的base变量不能在栈上分配,因为在栈上分配在函数调用结束后就销毁了,内部的匿名函数就引用了一个失效的变量,这是不允许的,所以闭包的环境中引用的变量不能够在栈上分配。

闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。由闭包的实质含义,我们可以推论:闭包获取捕获变量相当于引用传递,而非值传递;对于闭包函数捕获的常量和变量,无论闭包何时何处被调用,闭包都可以使用这些常量和变量,而不用关心它们表面上的作用域。

看下面这个例子

package main

import (
	"fmt"
)

func main() {
   
	add := func(base int) func(int) int {
   
		y := 1
		fmt.Printf("base变量地址%p\n", &base)
		fmt.Printf("y局部变量地址%p\n", &y)
		fmt.Printf("y局部变量值%v\n", y)

		return func(i int) int {
   
			y++
			fmt.Printf("闭包匿名函数内base变量地址%p\n", &base)
			fmt.Printf("闭包匿名函数内base变量值%v\n", base)
			fmt.Printf("闭包匿名函数内y局部变量地址%p\n", &y)
			fmt.Printf(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值