Go进阶(3): 函数+闭包+defer

1. 函数是一种数据类型

函数参数的传递方式有值传递和引用传递(指针/slicer/map/channel/interface)两种;不管哪一种,传递的都是值或者地址的副本;一般来说,地址传递的效率更高,因为数据量小,而值传递随着数据量的增加而效率降低(如结构体拷贝)。

示例:

package main

import (
	"fmt"
	"reflect"
)

func getSum(num1 int, num2 int) int {
	return num1+num2
}

func main() {
	funcVar := getSum
	fmt.Println("the type of getSum :", reflect.TypeOf(getSum),
		" the type of funcVar :",reflect.TypeOf(funcVar))

	res := getSum(10,20)
	fmt.Println("the result : ", res)
}

运行结果:

the type of getSum : func(int, int) int  

the type of funcVar : func(int, int) int

the result :  30

2. 函数可以作为形参

示例:

package main

import "fmt"

func getSum(num1 int, num2 int) int {
	return num1+num2
}

func myFunc(funcVar func(int, int)int, num1 int, num2 int) int {
	return funcVar(num1, num2)
	}

func main() {
	res := myFunc(getSum, 10,20)
	fmt.Println("the result : ", res)
}

运行结果:

the result :  30

3. 支持自定义数据类型

示例:

package main

import "fmt"
type myFunType func(int, int)int // //定义函数类型的类型名

func getSum(num1 int, num2 int) int {
	return num1+num2
}

func myFunc(funcVar myFunType, num1 int, num2 int) int {
	return funcVar(num1, num2)
}

func main() {
	type myInt int  //定义基本数据类型的类型名
	var num myInt = 40
	fmt.Println("num : ", num)

	res := myFunc(getSum, 10 , 20)
	fmt.Println("res : ", res)
}

运行结果:

num :  40
res :  30

4. 支持对函数返回值命名

示例:

package main

import "fmt"

func get_sum_sub(num1 int, num2 int) (sum int, sub int){
	sum = num1 + num2
	sub = num1 - num2
	return
}

func main() {
	sum, sub := get_sum_sub(20, 10)
	fmt.Println("sum : ", sum, "sub : ", sub)
}

运行结果:

sum :  30

sub :  10

5. 支持可变参数

func(args... int) sum int{}  // args是slice类型,可以通过args[index]访问各个值。

示例:

package main

import "fmt"

func getSum(num1 int, args... int) int{
	sum := num1
	for i := 0; i每一个源文件都可以包含一个init函数,该函数在main函之前被go框架运行。通常可以在init函数中完成初始化工作。

Note:

  • 如果一个文件同时包含全局变量定义 / init() / main()。其执行的流程是 全局变量定义 -> init() -> main()
  • init函数最大的作用是完成一些初始化的工作
  • 如果main.go和pkg.go, 执行流程 引用包的变量定义 -> 引用包的init() ->main()函数的变量定义 -> main()的init()
package main

import "fmt"

var gVar = gTest()

func gTest() string {
	fmt.Println("global variable:")
	return "global variable:"
}

func init() {
	fmt.Println("init function:")
}

func main() {
	fmt.Println("main function:")
}

运行结果:

global variable:

init function:

main function:

7. 匿名函数

Go也支持匿名函数,如果某个函数只希望使用一次,可以考虑匿名函数;当然,匿名函数也可以实现多次调用。

使用方式:

  • 在定义匿名函数时直接就使用
  • 将匿名函数赋给一个变量(函数变量),再通过该变量调用匿名函数
  • 全局匿名函数可以在程序的任何地方进行调用
package main

import "fmt"

func main() {
	// case 1:
	getSum := func (num1 int, num2 int) int {
		return num1 + num2
	}(10,20)
	fmt.Println("getSum = ", getSum)

	//case 2:
	getSub := func(num1 int, num2 int) int{
		return num1-num2
	}
	res := getSub(10,20)
	fmt.Println("getSub = ", res)
}

运行结果:

getSum =  30
getSub =  -10

8. 闭包

闭包就是一个函数其相关的引用环境的一个整体(实体)

package main

import "fmt"

func addUpper() func(int)int {
	var n int = 10 //外部变量
	return func (x int) int { //匿名函数引用外部变量 构成闭包
		n += x
		return n
	}
}
func main() {
	myFun := addUpper()
	fmt.Println("1st close function", myFun(1))
	fmt.Println("2nd close function", myFun(2))
	fmt.Println("3rd close function", myFun(3))
}

运行结果(每进行一次调用就进行累计,不会初始化):

1st close function 11
2nd close function 13
3rd close function 16
可以理解为:闭包是一个类,函数是操作,外部变量是字段

8. defer的使用技巧

在函数中,我们经常需要创建资源(如,数据库连接 / 文件句柄 / 锁等),为了在函数执行完毕后,可以及时释放资源,Go框架设计了defer。

Note:

  • 当go执行到一个defer时,不会立即执行defer后面的语句,而是将defer后面的语句压进栈中(可以理解为defer栈)
  • 当函数执行结束后,defer 语句出栈,遵循先入后出
  • 在defer将语句放入栈中时,也会将相关的值Copy,同时入栈
  • defer最主要的价值在于,当函数执行完毕后,可以及时地释放函数创建的资源,如defer file.close()  / defer connect.close()

示例:

package main

import "fmt"

func sum(num1 int, num2 int) int {
	defer fmt.Println("defer1 num1=", num1) //暂时不执行,压进defer栈中
	defer fmt.Println("defer2 num2=", num2)
	res := num1 + num2
	fmt.Println("resFun=", res)
	return res
}

func main() {
	res := sum(10,20)
	// 当函数执行完毕后,defer按照先进后出的方式出栈
	fmt.Println("res=",res)
}

运行结果:

resFun= 30
defer2 num2= 20
defer1 num1= 10
res= 30

你可能感兴趣的:(Go进阶(3): 函数+闭包+defer)