defer
延迟函数 (等其他语句执行完毕后, 再会去执行defer函数, 且defer里的语句会按照逆序执行 )
使用场景: 当文件打开(打开资源)之后 一般后面接上 defer 文件关闭~ (效果就是当文件操作做完之后, 文件关闭)
处理 程序报错: 异常(程序执行时发生), 错误(预期可以发现的)
package main
import "fmt"
func main() {
fp("1")
fmt.Println("2")
defer fp("3")
fmt.Println("4") // 输出结果 1243
}
func fp(str string) {
fmt.Println(str)
}
defer传参调用时机
package main
import "fmt"
func main() {
n := 10
fmt.Println("main n=", n)
defer fun1(n) // 说明defer 函数已经被调用, 但是没有执行, 参数是已经传递进去的, 是当其他语句执行完才会去执行defer函数
n++
fmt.Println("main ends n=", n)
// main n= 10
// main ends n= 11
// fun1函数中n= 10
}
func fun1(n int) {
fmt.Println("fun1函数中n=", n)
}
函数本身也是数据类型
函数() 就是调用函数, 不加(), 那么函数也是一个变量, 可以赋值
函数的类型是 创建函数时的类型, 可以赋值给相同类型的变量(or 类型相同的函数)
函数名: 指向函数体的内存地址, 是一种特殊类型的变量
匿名函数
Go 支持函数式编程~ -> 引出 回调函数 & 闭包结构
函数本身也是一种类型
package main
import "fmt"
func main() {
// 匿名函数, 在函数体后加(), 调用了这个函数, 匿名函数只能一次 (需要调用的)
func() {
fmt.Println("我是一个匿名函数")
}()
// 若将匿名函数进行赋值, 就可以实现多次调用 ~
f1 := func() {
fmt.Println("这是一个匿名函数赋值给其他")
}
f1()
f1()
// 匿名函数可以传参数么?
func(a, b int) {
fmt.Println(a, b)
}(1, 2)
// 匿名函数可以赋值给变量, 然后由变量传参调用么?
f2 := func(a, b int) {
fmt.Println(a, b)
}
f2(2, 3) // 2 3
f2(2, 5) // 2 5
// 匿名函数还可以定义返回值~ 无论是调用一次 还是赋值给变量都是可以的~
f3 := func(a, b int) int {
return a + b
}(3, 4)
fmt.Println(f3) // 7
f4 := func(a, b int) int {
return a * b
}
fmt.Println(f4(2, 3)) // 6
}
回调函数
- 将匿名函数作为另一个函数的参数 (高阶用法)
package main
import "fmt"
func main() {
// 正常的函数调用
r1 := add(1, 4)
fmt.Println(r1)
// 高阶函数调用
r2 := oper(1, 2, add)
fmt.Println(r2)
// 匿名函数
f := func(a, b int) int {
return a * b
}
fmt.Println(oper(4, 2, f)) // 结合匿名函数完成高阶函数的调用
// or 直接传递匿名函数
f2 := oper(1, 2, func(a int, b int) int {
if b == 0 {
fmt.Println("除数不能为0")
return 0
}
return a / b
})
fmt.Println(f2)
}
// 运算
// 高阶函数, 参数是接收另一个函数 (很灵活)
func oper(a, b int, fun func(int, int) int) int {
fmt.Println(a, b, fun)
r := fun(a, b)
return r
}
func add(a, b int) int {
return a + b
}
闭包结构
- 将匿名函数作为另一个函数的返回值, 形成闭包结构
闭包是将 函数内部和外部链接起来的一座桥梁
在一个外层函数中, 有内层函数, 而在内层函数中, 可以去操作外层函数的局部变量, 并且外层函数的返回值就是该内层函数 (解决了变量名冲突的问题)
这时候 局部变量的生命周期会发生变化, 闭包结构中的外层函数的局部变量并不会随外层函数结束而销毁, 因为内层函数还在使用
但由于垃圾回收器不会将闭包中的变量销毁, 所以可能会造成内存泄漏的隐患
闭包结果的返回值是一个函数, 这个函数可以调用闭包结构中的变量
package main
import "fmt"
// 闭包例子 -> 与正常的生命周期不一致 (变量作用域升级了~)
// js 仲很多框架都是闭包结构的 -> 防止变量冲突, 全局变量污染
// i 在没有被引用的时候or被垃圾 会被销毁
func main() {
r1 := increment()
fmt.Printf("r1的内存地址为%p\n", &r1) // 此时打印的是函数increment() r1 的地址
v1 := r1() // 执行内层函数
fmt.Println(r1())
fmt.Println(v1)
fmt.Println("==============")
r2 := increment() // 当再次调用函数时的时候, i = 0
fmt.Printf("r2的内存地址为%p\n", &r2) // 此时打印的是r2所指向的地址 发现与 r1所指向不一样
// 故可得出结论, 销毁的是外部函数, 但是其外部函数所定义的局部变量并没有被销毁
fmt.Println(r2()) // 1
fmt.Println(r1()) // i = 3
// 这里的 i 并没有随着第二次创建而被销毁, 而是在内层函数中继续被调用
}
// 自增函数
func increment() func() int { // 外层函数
i := 0 // 定义一个局部变量
// 在外层函数内部定义一个匿名函数!, 给变量自增并返回, 内部函数还在使用那个局部变量 i (还存在引用)
fun := func() int {
i++
return i
}
return fun
}
参数传递
值类型: 拷贝, 创建的时候, 拷贝一份 (开辟的是新的内存地址, 指向内存空间不同) 如 int, 数组, string, boolean, ...
引用类型: 操作的是数据的地址, 如 slice, map, chan... (指向同一片地址)