函数 | 青训营笔记

96 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

1. 格式

  1. func funcName([parameter list]) (return list) {}

2. 返回值

  1. Go语言可以有多个返回值(返回值可以命名也可以不命名),并且指定了返回值的名称后return语句可以不用写变量名,并且返回值只有一个参数并且参数名没有指定的情况下是可以不用()的
  2. 举几个栗子
// 1. 返回值未命名的情况
func add(a, b int) int {
    return a + b
}

// 2. 返回值命名的情况
func add1(a, b int) (m int) {
    m = a + b 
}

// 3. 有多个返回值
func compute(a, b int) (int, int) {
    return a + b, a - b
}
c, d := compute(1, 2)  // 使用两个变量来接收,不想接收的可以用_代替

// 4. 有多个返回值且有返回的参数名
func compute1(a, b int) (m int, n int) {
	m = a + b
    n = a - b
    return
}

3. 参数

  1. 实参和形参的概念,这里就不多解释了
  2. Go语言和Java都支持可变长的形参,下面举几个栗子
func varParameter(arg...string) {  // 这个时候的arg就是一个切片类型
    for _, val := range string {
        fmt.Println(val)
    }
}
  1. 值传递和引用传递,这个也不多说

4. 匿名函数

  1. 匿名函数也被称为"闭包",实际上就是没有函数名的函数,通常在变量传递的过程中使用
  2. 匿名函数的定义与调用,下面举几个栗子
// 1. 定义匿名函数并赋值给某个变量
f := func(a int) {
    fmt.Println("传进来的参数是:", a)
}
f(6)

// 2. 定义匿名函数并直接调用
func(a int) {
    fmt.Println("传进来的参数是:", a)
}(6)

// 3. 匿名函数做回调函数(同步回调)
func visit(list []int, f func(int)) {
	// for-range遍历切片
	for _, val := range list {
		f(val)
	}
}

func main() {
	visit([]int{1, 2, 3}, func(value int) {
		fmt.Println(value)
	})
}

// 4. 再举一个回调函数的例子(同步回调)
type FuncType func(int, int) int //先用FuncType代表有两个int型参数和有一个int型返回值的函数,不用管该函数是否存在
func add(a, b int) int { //加法
	return a + b
}
func minus(a, b int) int { //减法
	return a - b
}
func multiple(a, b int) int { //乘法
	return a * b
}
func divide(a, b int) int { //除法
	return a / b
}
func Calculate(a, b int, fType FuncType) (result int) { //fType表示一个函数类型变量
	result = fType(a, b)
	return
}
func main() {
	var x, y int
	fmt.Print("请输入两个数:")
	fmt.Scan(&x, &y)
	fmt.Println("这两个数之和为:", Calculate(x, y, add))
	fmt.Println("这两个数之差为:", Calculate(x, y, minus))
	fmt.Println("这两个数之积为:", Calculate(x, y, multiple))
	fmt.Println("这两个数之商为:", Calculate(x, y, divide))
}


// 举一个JS回调的例子(异步回调)
function 摇骰子(){
	setTimeout(()=>{
  	return parseInt(Math.random()*6) + 1
  },1000)
}

const n = 摇骰子() 
console.log(n)// n是Undefined 

function callback(x) {
	console.log(x)
}

摇骰子(callback){
	setTimeout(()=>{
  	callback(parseInt(Math.random()*6) + 1)
  },1000)
}

摇骰子(x=>{
console.log(x)
})

// 由于这里函数的参数和需要用到的值是一样的,所以还可以简化为
摇骰子(console.log)

/*
回调有同步回调和异步回调的区别
1. 回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2. 函数F1调用函数F2的时候,函数F1通过参数给函数F2传递了另外一个函数 F3 的指针,在函数F2执行的过程中,函数F2调用了函数F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数F3就是回调函数。

3. 如果写程序是调用系统的API,那么把关系反过来,你写一个函数,让系统调用你的函数,那就是回调了,那个被系统调用的函数就是回调函数。
*/

5. defer延迟语句

1. 为什么会有defer延迟语句

  1. 函数中经常需要创建资源(获取数据库连接等),为了在函数执行完毕以后可以及时释放资源,引入了defer延迟语句
  2. defer语句主要用在函数中,一般是函数执行结束前的最后一个操作,最后一个动作

2. 执行逻辑

  1. 遇到defer语句不会立即执行,会将语句按照顺序压入栈中,然后继续执行函数中的下一条语句
  2. 当函数执行完毕以后就会从栈中依次取出defer语句然后执行,先进去的先执行,后进去的后执行
  3. 注意defer压入的时候值也一并压入了

3. 一个例子

func Call() {
  defer fun1()
  defer fun2()
  defer fun3()
}

func func1() {
	fmt.Println("1")
}

func func2() {
	fmt.Println("2")
}

func func3() {
	fmt.Println("3")
}

func main() {
  Call()  // 最后打印的结果:3 2 1
}

3. defer和return的执行顺序

  1. defer在return后面执行

5. defer常见的应用场景

1. 关闭资源

  1. 比如说可以在获取资源后就写上defer语句用来释放资源

2. 和recover()函数一起使用

6. 常见的函数类型

  • 测试函数:函数名以Test开头,后面必须大写字母开头
  • 基准函数:函数名以Benchmark开头,后面必须大写字母开头
  • 示例函数:函数名以Example开头,后面必须大写字母开头
  • 普通函数

7. init函数

  • 每一个go文件都有一个init函数,在main函数执行之前执行,完成一个初始化的操作

8. 闭包

  1. 闭包实际上就是一个函数及其相关的引用环境构成的一个整体

8. 说明

  • Go语言中的函数不支持重载
  • Go语言中的函数也是一种类型,可以赋值给一个变量,使用这个变量就相当于是使用这个函数