闭包通常指变量的生命周期,闭包指的是返回一个函数的时候用来外面的闭包。通常一个函数调用完成后是需要销毁的,但在被内部作用域引用的情况下,是不能进行销毁的。
1.函数-闭包
- 示例
一般而言,在调用一个匿名函数的时候,通常如下:
add30 := func(n int) int {
return n + 30
}
匿名函数赋值给add30
,返回值是传递的行参加30.
比如有多个,写多次
add30 := func(n int) int {
return n + 30
}
add20 := func(n int) int {
return n + 20
}
add10 := func(n int) int {
return n + 10
}
fmt.Println(add30(10))
fmt.Println(add20(10))
fmt.Println(add10(10))
运行
[root@linuxea.com /opt/Golang/work3]# go run add13.go
40
30
20
现在,换种写法.
定义一个addBase,在匿名函数中定义一个int类型的base行参,作为基数,返回一个int类型的函数类型。如下:
addBase := func(base int) func(int) int {}
而后在其中返回.
首先返回值的类型是一个int的函数,那么就返回一个int类型的函数签名
addBase := func(base int) func(int) int {
return func(n int) int{
}
}
在返回的函数类型中,在return base + n
addBase := func(base int) func(int) int {
return func(n int) int{
return base + n
}
}
在return base + n
中,base是匿名函数中的基数的变量,return base + n
返回的就是匿名函数的基数base函数+返回值中的n
假如现在对addBase
进行传参,比如传递一个实参,如8,addBase(8)
,这样就变成了, 8 +n
,传递的8是base
赋值一个变量进行调用
add1 := addBase(8)
fmt.Printf("%Tn",add1)
add1现在也是一个函数类型。并且进行传值,而后打印
fmt.Println(add1(8))
代码块
package main
import "fmt"
func main(){
addBase := func(base int) func(int) int {
return func(n int) int{
return base + n
}
}
add1 := addBase(8)
fmt.Printf("%Tn",add1)
fmt.Println(add1(8))
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add13.go
func(int) int
16
或者这样
package main
import "fmt"
func main(){
addBase := func(base int) func(int) int {
return func(n int) int{
return base + n
}
}
fmt.Println(addBase(8)(8))
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add14.go
16
2.函数-值类型引用类型
对于值类型和引用类型的区分,主要看变量赋值给新的变量后,修改新变量后如果对旧变量有影响,就是引用类型,如果没有影响就是值类型
针对值类型和引用类型在赋值后新旧变量的地址并不相同,只是引用类型在底层共享数据结构(其中包含指针类型元素)
值类型:int(数值),bool(布尔),float(浮点数),array,指针,数组,结构体
引用类型:slice(切片),map(映射),接口等
- 示例
创建两个切片分别是array和slice,而后赋值给sliceA和arrayA,并且修改sliceA和arrayA的索引0位置的A修改成Z,而后打印结果
package main
import "fmt"
func main(){
array := [3]string{"A","B","C"}
slice := []string{"A","B","C"}
arrayA := array
sliceA := slice
arrayA[0] = "Z"
sliceA[0] = "Z"
fmt.Println(arrayA,array)
fmt.Println(sliceA,slice)
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add16.go
[Z B C] [A B C]
[Z B C] [Z B C]
对于值类型,要修改需要通过指针来操作
-
示例
定义age,分别通过赋值修改,和指针操作来修改值。如下:
package main
import "fmt"
func main(){
age := 30
ageA := age
ageA = 31
fmt.Println(ageA,age)
pointer := &age
*pointer = 31
fmt.Println(*pointer,age)
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add17.go
31 30
31 31
最终指针修改会修改会影响到旧变量
3.函数-值传递指针传递
在go中只有值传递
- 值传递
在go语言中参数传递默认为值传递(形参为实参变量的副本),对于引用类型数据因其底层共享数据结构,所以在函数内可对引用类型数据修改从而影响函数外的原变量信息。如下图
a赋值给b后,b是a复制的一份,而s1赋值给s2仍然共享底层数据
- 指针传递
指针赋值给函数后,将会指到原来的地址,并修改内存中的数据。变量的值也就变了。行参和实参都是通过拷贝的,只不过值的数据是一样的,但是值的地址指向是不一样的。
- 示例
值传递赋值的都是一个副本。引用类型数据因其底层共享数据结构。
创建一个changeInt
和changeSlice
函数,分别是int类型,和切片,而后分别赋值不同的类型元素通过参数传递给函数,而后打印结果
package main
import "fmt"
func changeInt(a int){
a = 100
}
func changeSlice(s []int){
s[0] = 100
}
func main(){
num := 1
changeInt(num)
fmt.Println(num)
nums := []int{1,2,3}
changeSlice(nums)
fmt.Println(nums)
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add18.go
1
[100 2 3]
值类型传递并没有修改,说明changeInt
修改没有影响,参数传递通过值类型传递给不同的地址。而引用类型传递后被修改,changeSlice
修改完成,这些参数传递也是值类型传递,但是会指向同一一个内存区间。
如果此时要修改值类型,就需要指针操作
- 指针传递
在原有的代码中添加一个changeIntByPoint
函数
func changeIntByPoint(a *int){}
而后通过指针操作
*a = 100
如下:
package main
import "fmt"
func changeInt(a int){
a = 100
}
func changeSlice(s []int){
s[0] = 100
}
func changeIntByPoint(a *int){
*a = 100
}
func main(){
num := 1
changeInt(num)
fmt.Println(num)
nums := []int{1,2,3}
changeSlice(nums)
fmt.Println(nums)
changeIntByPoint(&num)
fmt.Println(num)
}
运行
[root@linuxea.com /opt/Golang/work3]# go run add19.go
1
[100 2 3]
100
现在值就变成了100