这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
1. 格式
- func funcName([parameter list]) (return list) {}
2. 返回值
- Go语言可以有多个返回值(返回值可以命名也可以不命名),并且指定了返回值的名称后return语句可以不用写变量名,并且返回值只有一个参数并且参数名没有指定的情况下是可以不用()的
- 举几个栗子
// 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. 参数
- 实参和形参的概念,这里就不多解释了
- Go语言和Java都支持可变长的形参,下面举几个栗子
func varParameter(arg...string) { // 这个时候的arg就是一个切片类型
for _, val := range string {
fmt.Println(val)
}
}
- 值传递和引用传递,这个也不多说
4. 匿名函数
- 匿名函数也被称为"闭包",实际上就是没有函数名的函数,通常在变量传递的过程中使用
- 匿名函数的定义与调用,下面举几个栗子
// 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延迟语句
- 函数中经常需要创建资源(获取数据库连接等),为了在函数执行完毕以后可以及时释放资源,引入了defer延迟语句
- defer语句主要用在函数中,一般是函数执行结束前的最后一个操作,最后一个动作
2. 执行逻辑
- 遇到defer语句不会立即执行,会将语句按照顺序压入栈中,然后继续执行函数中的下一条语句
- 当函数执行完毕以后就会从栈中依次取出defer语句然后执行,先进去的先执行,后进去的后执行
- 注意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的执行顺序
- defer在return后面执行
5. defer常见的应用场景
1. 关闭资源
- 比如说可以在获取资源后就写上defer语句用来释放资源
2. 和recover()函数一起使用
6. 常见的函数类型
- 测试函数:函数名以Test开头,后面必须大写字母开头
- 基准函数:函数名以Benchmark开头,后面必须大写字母开头
- 示例函数:函数名以Example开头,后面必须大写字母开头
- 普通函数
7. init函数
- 每一个go文件都有一个init函数,在main函数执行之前执行,完成一个初始化的操作
8. 闭包
- 闭包实际上就是一个函数及其相关的引用环境构成的一个整体
8. 说明
- Go语言中的函数不支持重载
- Go语言中的函数也是一种类型,可以赋值给一个变量,使用这个变量就相当于是使用这个函数