1. 函数的定义
Go中的函数分为:自定义函数 和 系统函数
- 函数的基本语法
func 函数名称 (形参列表) (返回值列表) {
逻辑语句
return 返回值列表
}
- 示例
package main
import "fmt"
func main() {
var a = 1
var b = 2
sum := add(a , b)
fmt.Printf("a+b=%v\n",sum)
}
func add(a int,b int) int {
return a + b
}
2. 函数传参方式
包括 值传递 和 引用传递。它们传递个函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是变量的内存地址拷贝。
- 值类型:基本数据类型 int 系列、float 系列、bool、string、数组 和 结构体 struct
- 引用类型:指针、切片、map、管道、interface 等引用类型
3. 匿名函数
Go 支持匿名函数,匿名函数就是没有名字的函数
3.1 匿名函数方式一
- 定义匿名函数是就直接调用,但它只能被调用一次
package main
import "fmt"
func main() {
// 匿名函数
result := func (n1 int, n2 int) int {
return n1 + n2
}(1, 2) // 匿名函数传参
fmt.Printf("n1+n2=%v\n",result)
}
3.2 匿名函数方式二
package main
import "fmt"
func main() {
add := func (n1 int, n2 int) int {
return n1 + n2
}
fmt.Printf("n1+n2=%v\n", add(1,2))
fmt.Printf("n1+n2=%v\n", add(3,4))
}
3.3 全局匿名函数
package main
import "fmt"
// 全局匿名函数
var (
testAdd = func(n1 int, n2 int) int {
return n1 + n2
}
)
func main() {
res := testAdd(2, 4)
fmt.Printf("n1+n2=%v\n", res)
}
4. init 函数
每一个go文件都可以包含一个init函数,该函数会在main函数执行前被调用!主要是完成一些初始化工作,比如配置文件加载等
package main
import "fmt"
func init() {
fmt.Println("调用init函数!")
}
func main() {
fmt.Println("执行main方法业务逻辑!")
}
- 如果一个文件同时包含 全局变量定义、init函数和 main函数,则执行顺序是:全局变量定义 --> init函数 --> main函数
package main
import "fmt"
var global = test()
func test() int {
fmt.Println("全局变量")
return 88
}
func init() {
fmt.Println("调用init函数!")
}
func main() {
fmt.Println("执行main方法业务逻辑!")
}
- 在 main.go 和 utils.go 中都有 全局变量定义、init函数和 main函数,则在 main.go 中
5. 内置函数
golang 中提供了一些内置函数,方便开发者使用
- func len : 用来求长度,使用与 字符串、数组、切片、map、管道
func len(v Type) int
- func new :用来分配值类型的内存地址,比如 int、float32、struct 等,返回的是指针
func new(Type) *Type
案例
package main
import "fmt"
func main() {
n := 1
// n的类型int ,n的值=1, n的内存地址=0xc00012a008
fmt.Printf("n的类型%T ,n的值=%v, n的内存地址=%v\n", n, n, &n)
n2 := new(int) // 返回值是 *int,即是一个 int 型指针
// 修改指针的值
*n2 = 2
// n2的类型*int ,n2的值=0xc00012a020, n2的内存地址=0xc000124020, n2指针指向的值=2
fmt.Printf("n2的类型%T ,n2的值=%v, n2的内存地址=%v, n2指针指向的值=%v\n", n2, n2, &n2, *n2)
}
- func make :用来分配引用类型的内存地址,比如 map、管道、切片
- 其他的内置函数
func delete(m map[Type]Type1, key Type)
func copy(dst, src []Type) int
func append(slice []Type, elems ...Type) []Type
func close(c chan<- Type)
6. 函数使用细节
- 函数的形参列表可以是多个,返回值列表也可以是多个
- 函数首字母不能是数字,首字母大写的函数可以被当前 package 中文件和其他 package 中的文件使用。首字母小写的只能被当前 package 中的文件引用
- 基本数据类型 和 数组 的形参都是值传递,在函数内部修改参数,不会影响到原来的值
package main
import "fmt"
func add(n1 int) {
n1 = n1 + 10
fmt.Printf("n1=%v\n",n1)
}
func main() {
num := 20
add(num)
// num 的值不会被改变
fmt.Printf("num=%v\n",num)
}
如果要改变 num 的值该怎么办?可以传入变量的地址 &
package main
import "fmt"
func add(n1 *int) {
*n1 = *n1 + 10
fmt.Printf("n1=%v\n",n1)
}
func main() {
num := 20
add(&num)
fmt.Printf("num=%v\n",num)
}
为什么能实现这个功能? 因为 num 和 n1 指向的都是同一个内存地址
- Go中的函数不支持重载
- Go中函数也是一种数据类型,可以赋值给一个变量,通过该变量可以对函数调用
package main
import "fmt"
func sub(n1 int, n2 int) int {
return n2 - n1
}
func main() {
// 函数赋值给一个变量
a := sub
fmt.Printf("a的类型=%T, sub的类型=%T\n",a, sub)
// 通过变量调用函数
res := a(2, 10)
fmt.Printf("res=%v\n",res)
}
- Go中函数既然是一种变量,那么它就可以作为函数的形参进行传递并调用
package main
import "fmt"
func add(n1 int, n2 int) int{
return n1 + n2
}
// 可以接受函数形参
func myFun(funvar func(int,int) int, n1 int, n2 int) int{
return funvar(n1,n2)
}
func main() {
res := myFun(add, 3, 5)
// res=8
fmt.Printf("res=%v\n", res)
}
- Go 中支持对函数返回值命名
// 返回值的名称分表是 f 和 err
func ParseFloat(s string, bitSize int) (f float64, err error)
- Go 中使用
_标识符忽略函数的返回值 - Go 中支持可变参数
func add(args... int) int {
}
func sum(n1 int, args... int) int {
}
示例:
package main
import "fmt"
func main() {
// 多参数传递
res := add(1, 10, 20, 30)
// res=61
fmt.Printf("res=%v\n",res)
}
func add(n1 int, args... int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
7.闭包
闭包就是一个函数和*该函数相关的引用**组成的一个整体。举个例子:
package main
import (
"fmt"
)
// Go中函数也是数据类型,所以 add 函数的返回值也是函数,即:func(int) int
func add() func(int) int {
var n int = 1
var str = "hello"
// 返回了一个匿名函数
return func(x int) int {
n = n + x
str += "a"
fmt.Println("str=",str)
return n
}
}
func main() {
f := add()
// 匿名函数的调用方式:通过函数变量来调用
fmt.Println(f(1)) // 2
fmt.Println(f(2)) // 4
}
- 匿名函数引用到了匿名函数外的 n 变量,因此匿名函数和 n 就形成了一个整体,叫做闭包
// Go中函数也是数据类型,所以 add 函数的返回值也是函数,即:func(int) int
func add() func(int) int {
var n int = 1
// 返回了一个匿名函数
return func(x int) int {
n = n + x
return n
}
}
- 需要注意一点,这段代码里变量
n和str都只被初始化了一次,所以看到了上图所示的n的累加输出结果 - 对闭包可以换个角度理解:闭包是
Java中的类,n是类的属性,函数是类的方法,这样好理解对n的累加操作
7.1 使用场景
- 实现一个功能,给所有的文件名统一加后缀,比如
.jpg。- 传入的文件名可以带有
.jpg,也可以不带有.jpg - 最终要求返回统一的文件名称
- 传入的文件名可以带有
package main
import (
"fmt"
"strings"
)
func main() {
f := appendFileName(".jpg")
fmt.Println("文件处理后的名称=", f("world"))
fmt.Println("文件处理后的名称=", f("hello.jpg"))
}
func appendFileName(suffix string) func (string) string {
return func (name string) string {
// 如果 name 没有指定后缀,则加上并返回
if(!strings.HasSuffix(name, suffix)) {
return name + suffix
}
return name
}
}