defer函数、匿名函数、回调函数与闭包

357 阅读5分钟

一、defer函数

1.1 延迟是什么?

即延迟(defer)语句,延迟语句被用于执行一个函数调用,在这个函数之前,延迟语句返回。

1.2 延迟函数

你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题

  • 如果有很多调用defer,那么defer是采用后进先出模式
  • 在离开所在的方法时,执行(报错的时候也会执行)
func ReadWrite() bool {
    file.Open("file")
    defer file.Close()
    if failureX {
          return false
    } i
    f failureY {
          return false
    } 
    return true
}

最后才执行file.Close()

示例代码:

package main

import "fmt"

func main() {
	a := 1
	b := 2
	defer fmt.Println(b)
	fmt.Println(a)
}

运行结果:

1
2

示例代码:

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

运行结果:

Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest 

1.3 延迟方法

延迟并不仅仅局限于函数。延迟一个方法调用也是完全合法的。让我们编写一个小程序来测试这个。

示例代码:

package main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}

运行结果:

Welcome John Smith 

1.4 延迟参数

延迟函数的参数在执行延迟语句时被执行,而不是在执行实际的函数调用时执行。

让我们通过一个例子来理解这个问题。

示例代码:

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

运行结果:

value of a before deferred function call 10  
value of a in deferred function 5 

1.5 堆栈的推迟

当一个函数有多个延迟调用时,它们被添加到一个堆栈中,并在Last In First Out(LIFO)后进先出的顺序中执行。

我们将编写一个小程序,它使用一堆defers打印一个字符串。示例代码:

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Orignal String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

运行结果:

Orignal String: Naveen  
Reversed String: neevaN 

1.6 defer注意点

defer函数:
当外围函数中的语句正常执行完毕时,只有其中所有的延迟函数都执行完毕,外围函数才会真正的结束执行。
当执行外围函数中的return语句时,只有其中所有的延迟函数都执行完毕后,外围函数才会真正返回。
当外围函数中的代码引发运行恐慌时,只有其中所有的延迟函数都执行完毕后,该运行时恐慌才会真正被扩展至调用函数。

二、匿名函数

匿名函数:没有名字的函数。

定义一个匿名函数,直接进行调用。通常只能使用一次。也可以使用匿名函数赋值给某个函数变量,那么就可以调用多次了。

Go语言是支持函数式编程:

1.将匿名函数作为另一个函数的参数,回调函数

2.将匿名函数作为另一个函数的返回值,可以形成闭包结构。

 示例代码:

package main

import "fmt"

func main() {
	 fun1()
	 fun1()
	 fun2 := fun1
	 fun2()

	 //匿名函数
	 func (){
	 	fmt.Println("我是一个匿名函数。。")
	 }()

	 fun3:=func(){
	 	fmt.Println("我也是一个匿名函数。。")
	 }
	 fun3()
	 fun3()

	 //定义带参数的匿名函数
	 func (a,b int){
	 	fmt.Println(a,b)
	 }(1,2)

	 //定义带返回值的匿名函数
	 res1 := func (a, b int)int{
	 	return a + b
	 }(10,20) //匿名函数调用了,将执行结果给res1
	 fmt.Println(res1)

	 res2 := func (a,b int)int{
	 	return a + b
	 } //将匿名函数的值,赋值给res2
	 fmt.Println(res2)

	 fmt.Println(res2(100,200))
}

func fun1(){
	fmt.Println("我是fun1()函数。。")
}

执行结果:

我是fun1()函数。。
我是fun1()函数。。
我是fun1()函数。。
我是一个匿名函数。。
我也是一个匿名函数。。
我也是一个匿名函数。。
1 2
30
0xd2b460
300

三、回调函数

高阶函数: 根据go语言的数据类型的特点,可以将一个函数作为另一个函数的参数。 fun1(),fun2() 将fun1函数作为了fun2这个函数的参数。

fun2函数:就叫高阶函数 接收了一个函数作为参数的函数,高阶函数

fun1函数:回调函数 作为另一个函数的参数的函数,叫做回调函数。

代码示例:

package main

import "fmt"

func main() {
	//设计一个函数,用于求两个整数的加减乘除运算
	fmt.Printf("%T\n", add)  //func(int, int) int
	fmt.Printf("%T\n", oper) //func(int, int, func(int, int) int) int

	res1 := add(1, 2)
	fmt.Println(res1)

	res2 := oper(10, 20, add)
	fmt.Println(res2)

	res3 := oper(5,2,sub)
	fmt.Println(res3)

	fun1:=func(a,b int)int{
		return a * b
	}

	res4:=oper(10,4,fun1)
	fmt.Println(res4)


	res5 := oper(100,8,func(a,b int)int{
		if b == 0{
			fmt.Println("除数不能为零")
			return 0
		}
		return a / b
	})
	fmt.Println(res5)

}
func oper(a, b int, fun func(int, int) int) int {
	fmt.Println(a, b, fun) //打印3个参数
	res := fun(a, b)
	return res
}

//加法运算
func add(a, b int) int {
	return a + b
}

//减法
func sub(a, b int) int {
	return a - b
}

 运行结果:

func(int, int) int
func(int, int, func(int, int) int) int
3
10 20 0xe6d0a0
30
5 2 0xe6d0c0
3
10 4 0xe6cc40
40
100 8 0xe6cc60
12

四、闭包

go语言支持函数式编程: 支持将一个函数作为另一个函数的参数, 也支持将一个函数作为另一个函数的返回值。

闭包(closure): 一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量(外层函数中的参数,或者外层函数中直接定义的变量),并且该外层函数的返回值就是这个内层函数。 这个内层函数和外层函数的局部变量,统称为闭包结构。

局部变量的生命周期会发生改变,正常的局部变量随着函数调用而创建,随着函数的结束而销毁。 但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还要继续使用。 

代码示例:

package main

import "fmt"

func main() {
	 res1 := increment() //res1 = fun
	 fmt.Printf("%T\n",res1) //func() int
	 fmt.Println(res1)
	 v1 := res1()
	 fmt.Println(v1) //1
	 v2 := res1()
	 fmt.Println(v2) //2
	 fmt.Println(res1())
	 fmt.Println(res1())
	 fmt.Println(res1())
	 fmt.Println(res1())

	 res2 := increment()
	 fmt.Println(res2)
	 v3 :=res2()
	 fmt.Println(v3) //1
	 fmt.Println(res2())

	 fmt.Println(res1())
}

func increment()func()int{ //外层函数
	//1.定义了一个局部变量
	i := 0
	//2.定义了一个匿名函数,给变量自增并返回
	fun := func ()int{ //内层函数
		i++
		return i
	}
	//3.返回该匿名函数
	return fun
}

 运行结果: ​

func() int
0x41d100
1
2
3
4
5
6
0x41d0e0
1
2
7

​ ​