Golang函数
Golang 函数
1、基本概念
为完成某一功能的程序指令(语句)的集合,称为函数
2、语法
func 函数名 (形参列表) (返回值列表) {
// 执行语句
return 返回值列表
}
- 形参列表: 表示函数的输入
- 函数中的语句: 表示为了实现某一功能代码块
- 函数可以有返回值,也可以没有
3、return 语句
- 如果返回多个值时,在接收时,希望忽略某个值时,使用
_
符号表示占位忽略 - 如果返回值只有一个, 返回值类型列表可以不写括号
4、注意事项
4.1、参数个数
函数的形参列表可以是多个,返回值列表也可以是多个
4.2、参数类型
形参列表和返回值列表的数据类型可以是值类型和引用类型。
4.3、作用域
函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,类似public,首字母小写,只能被本包文件使用,其它包文件不能使用,类似private
4.4、函数内变量
函数中的变量是局部的,函数外不生效
4.5、值传递和引用传递
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
传入变量的地址&,函数内以指针的方式操作变量。
func test(n *int){
*n = *n + 10
fmt.Printf("n = %d", *n)
}
func main(){
n := 20
// 传入地址
test(&n)
fmt.Printf("n = %d", *n)
}
4.6、不支持函数重载
4.7、函数类型
在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用
fun getSum(n1 int, n2 int) int {
return n1 + n2
}
func main(){
a := getSum
// 输出 func getSum(int int) int
fmt.Printf("a的类型为%T\n", a)
// 调用函数类型 等价于 getSum(11, 22)
res := a(11, 22)
// 函数当作形式参数传入
res = myFun(a, 11, 22)
}
// 带函数类型的形参
func myFun(funvar func(int int) int, num1 int, num2 int) int{
return funvar(num1, num2)
}
4.8、为了简化数据类型定义,Go支持自定义数据类型
type 自定义数据类型名 数据类型
// 定义一个函数类型
type myFunType func(int, int) int
func myFun2(funvar myFunType, num1 int, num2 int) int {}
4.9、函数返回值命名
func getSumAndSub(n1 int, n2 int) (sum int, sub int){
// 参数已经定义,直接赋值
sub = n1 - n2
sum = n1 + n2
// 不需要返回值
return
}
4.10、忽略返回值
使用_标识符,忽略返回值
4.11、可变参数
package main
import fm "fmt"
func test(n1 int, args ...int) (sum int) {
sum = n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return
}
func main() {
fm.Println(test(1, 2, 3))
}
5、init 函数
5.1、介绍
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用。
5.2、注意
-
如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程全局变量定义->init函数->main函数
-
init函数最主要的作用,就是完成一些初始化的工作
-
引入包的init方法先执行
6、匿名函数
6.1、介绍
Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
6.2、创建方式
6.2.1、 直接调用
func main(){
res = func (n1 int, n2 int) int {
return n1 + n2
}(10, 20)
}
6.2.2、赋值给变量
func main(){
res = func (n1 int, n2 int) int {
return n1 + n2
}
a := res
a(10, 30)
}
6.2.3、全局匿名函数
var (
fun1 = func (n1 int, n2 int) int {
return n1 + n2
}
)
func main(){
fun1(10, 30)
}
7、闭包
7.1、介绍
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
7.2、案例
func AddUpper func (int) int {
var n int = 10
return func (x int) int {
n += x
return n
}
}
- AddUpper是一个函数,返回的数据 类型是fun(int)int
- 返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包。
- 可理解为: 闭包是类,函数是操作,n是字段。函数和它使用到n构成闭包。
- 当我们反复的调用f函数时,因为n是初始化一次,因此每调用一次就进行累计。
- 我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。
8、函数的defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制)。
8.1、细节
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中[我为了讲课方便,暂时称该栈为defer栈],然后继续执行函数下一个语句。
- 当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈先入后出的机制),所以同学们看到前面案例输出的顺序
- 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈
8.2、最佳实战
defer最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源
9、函数参数传递方式
9.1、介绍
值类型参数默认就是值传递,而引用类型参数默认就是引用传递
9.2、方式
1)值传递
2)引用传递其实
不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低
9.3、值类型和引用类型
1)值类型:
基本数据类型int系列,float系列,bool,string、数组和结构体struct
2)引用类型:
指针、slice切片、map、管道chan、interface等都是引用类型
10、变量作用域
-
函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
-
函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效
-
如果变量是在一个代码块,比如for/if中,那么这个变量的的作用域就在该代码块