目录
- 一.init初始化函数
- 1 初始化函数作用
- 2 初始化函数定义案例
- 2.1 初始化项目
- 2.2 utils.go源代码
- 2.3 main.go源代码
- 二.匿名函数
- 1 匿名函数概述
- 2 匿名函数案例
- 三.闭包函数
- 1 闭包函数概述
- 2 闭包函数案例之返回上级函数内部变量
- 3 闭包函数案例之返回上级函数形参变量
- 4 闭包函数返回多个匿名函数案例
- 5 闭包函数实现给文件加后缀
- 四.高阶函数
- 1 高阶函数概述
- 2 高阶函数案例之函数作为参数案例
- 3 高阶函数案例之函数作为返回值
- 五.defer关键字
- 1 defer执行时机
- 2 defer案例
- 3 defer面试题
- 3.1 观察代码手写运算结果1
- 3.2 观察代码手写运算结果2
一.init初始化函数
1 初始化函数作用
- 1.init初始化函数可以用来进行初始化操作每个"*.go"源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。- 2.全局变量定义,init函数,main函数的执行流程?顺序依次是: 全局变量定义,init函数,main函数。- 3.多个源文件都有init函数,如何执行?
2 初始化函数定义案例
2.1 初始化项目
yinzhengjie@localhost 03-init % go mod init yinzhengjie-utlis
go: creating new go.mod: module yinzhengjie-utlis
go: to add module requirements and sums:go mod tidy
yinzhengjie@localhost 03-init %
2.2 utils.go源代码
package utilsimport "fmt"var (Name stringAge intGender string
)func init() {fmt.Println("in utils package ... init ")Age = 18Name = "Jason Yin"Gender = "boy"
}
2.3 main.go源代码
package mainimport ("fmt"// 第1步: 先导入第三方包"yinzhengjie-utlis/utils"
)// 第2步: 全局变量定义
var number int = demo()func demo() int {fmt.Println("in demo ...")return 100
}// 第3步: 调用init函数
func init() {fmt.Println("init函数被调用...")
}// 第4步: 调用main函数
func main() {fmt.Println("main函数被调用...")fmt.Printf("姓名:%s 年龄:%d 性别: %s\n", utils.Name, utils.Age, utils.Gender)
}
二.匿名函数
1 匿名函数概述
- Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数。- 匿名函数使用方式:- 1.在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次;- 2.将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数;- 如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了。
2 匿名函数案例
package mainimport "fmt"// 3.如果匿名函数是全局变量则可以被全局调用哟~
var mul = func(a, b int) int {return a * b
}func main() {var (x int = 7y = 5)// 1.定义匿名函数,定义的同时调用sum := func(a int, b int) int {return a + b}(x, y)// 2.将匿名函数赋值给一个变量,这个变量实际就是函数类型的变量sub := func(a, b int) int {return a - b}result01 := sub(x, y)result02 := mul(x, y)fmt.Printf("sum = %d\n", sum)fmt.Printf("sub = %d\n", result01)fmt.Printf("mul = %d\n", result02)
}
三.闭包函数
1 闭包函数概述
- 什么是闭包函数:闭包就是一个函数和其他相关的引用环境组合的一个整体。- 闭包的本质:闭包本质依旧是一个匿名函数,只是这个函数引用外界的变量/参数,因此我们说: "匿名函数 + 引用外界的变量/参数 = 闭包"。- 闭包函数特点:- 1.返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数,因此这个匿名函数就和变量/参数形成一个整体,构成闭包;- 2.闭包中使用的变量/参数会一直保存在内存中,所以会一直使用,意味着闭包不可滥用;- 闭包的应用场景:闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。
2 闭包函数案例之返回上级函数内部变量
package mainimport ("fmt"
)// 闭包指的是一个函数和与其相关的引用环境组合而成的实体。
func getSum() func(int) int {// 此变量属于getSum函数var sum int = 0// 此处的闭包: "返回的是一个匿名函数+函数以外的变量sum"return func(x int) int {sum += xreturn sum}
}func getSum2(sum, x int) int {sum += xreturn sum
}func main() {// 不使用闭包的时候,想要保留sum值,但不可以反复使用,因此每次调用都需要重新传入sum的值fmt.Println(getSum2(0, 10))fmt.Println(getSum2(10, 20))fmt.Println(getSum2(30, 30))fmt.Println("----- 分割线 -----")// 定义了一个函数变量,闭包返回的匿名函数引用的那个变量会一直保存在内存中,可以一直使用。var f1 = getSum()// 闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。fmt.Println(f1(10))fmt.Println(f1(20))fmt.Println(f1(30))fmt.Println("----- 分割线 -----")// 此处重新定义了一个函数变量哟,注意,f2和上面的f1函数是两个独立的匿名函数哟~f2 := getSum()fmt.Println(f2(40))fmt.Println(f2(50))}
3 闭包函数案例之返回上级函数形参变量
package mainimport ("fmt"
)// 闭包函数不仅仅可以返回函数内部变量,也可以直接返回形参变量
func getSum(sum int) func(int) int {return func(x int) int {sum += xreturn sum}
}func main() {// 定义了一个函数变量,闭包返回的匿名函数引用的那个变量会一直保存在内存中,可以一直使用。var f1 = getSum(0)// 闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。fmt.Println(f1(10))fmt.Println(f1(20))fmt.Println(f1(30))}
4 闭包函数返回多个匿名函数案例
package mainimport ("fmt"
)func calc(base int) (func(int) int, func(int) int) {add := func(i int) int {base += ireturn base}sub := func(i int) int {base -= ireturn base}return add, sub
}func main() {// 闭包其实并不复杂,只要牢记闭包=函数+引用环境。f1, f2 := calc(10)fmt.Println(f1(1), f2(2))fmt.Println(f1(3), f2(4))fmt.Println(f1(5), f2(6))
}
5 闭包函数实现给文件加后缀
package mainimport ("fmt""strings"
)// 我们可以利用闭包函数,给文件加后缀
func makeSuffixFunc(suffix string) func(string) string {return func(name string) string {if !strings.HasSuffix(name, suffix) {return name + suffix}return name}
}func main() {jpgFunc := makeSuffixFunc(".jpg")txtFunc := makeSuffixFunc(".txt")fmt.Println(jpgFunc("yinzhengjie"))fmt.Println(txtFunc("yinzhengjie"))
}
四.高阶函数
1 高阶函数概述
- 什么是高阶函数指的是: 一个函数的参数是函数,或者返回值是函数,满足其中一个就是高阶函数。闭包函数由于返回值是"匿名函数",因此我们说闭包也算得上是高阶函数的一个分支,但"高阶函数"不一定是匿名函数哟~- 高阶函数的应用场景:- 1.函数的参数或返回值可以使用函数,用于实现回调函数等功能;- 2.高阶函数可以用于实现递归函数,比如斐波拉契数列;- 3.高阶函数常常用于函数式编程,如列表的map,filter,reduce等函数; - 3.可用于实现柯力化函数,比如: 技术实现函数的复用和参数的延迟传递;- 4.部分应用(partial application),比如:将一个函数的参数固定下来,得到一个新的函数,继续调用;- 5.函数可以接受一个函数作为参数,然后根据这个函数的不同实现进行不同的处理;- 6.可以用函数组合技术将多个函数组合成一个新的函数;
2 高阶函数案例之函数作为参数案例
package mainimport ("fmt"
)func add(x, y int) int {return x + y
}func sub(x, y int) int {return x - y
}// 定义的形参中,要求传递的op变量是一个函数哟~
func calc(x, y int, op func(int, int) int) int {return op(x, y)
}func main() {var (a int = 100b int = 20)// 函数可以作为参数sum := calc(a, b, add)sub := calc(a, b, sub)fmt.Printf("%d + %d = %d\n", a, b, sum)fmt.Printf("%d - %d = %d\n", a, b, sub)
}
3 高阶函数案例之函数作为返回值
package mainimport ("errors""fmt"
)func add(x, y int) int {return x + y
}func sub(x, y int) int {return x - y
}// 函数也可以作为返回值
func do(s string) (func(int, int) int, error) {switch s {case "+":return add, nilcase "-":return sub, nildefault:err := errors.New("无法识别的操作符")return nil, err}
}func main() {var (a int = 100b int = 20)// 注意,此处返回的sum和sub都是函数哟~sum, _ := do("+")sub, _ := do("-")fmt.Printf("%d + %d = %d\n", a, b, sum(a, b)) // 调用sum函数fmt.Printf("%d - %d = %d\n", a, b, sub(a, b)) // 调用sub函数
}
五.defer关键字
1 defer执行时机
- 什么是defer:- 1.Go语言中的"defer"语句会将其后面跟随的语句进行延迟处理;- 2.在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,先被defer的语句最后被执行,最后被defer的语句,最先被执行。- 3.在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。- defer的作用:在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字。- defer的执行机制:defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如上图所示。- defer应用场景:比如你想关闭某个使用的资源,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事。
2 defer案例
package mainimport "fmt"func getSum(a, b int) (sum int) {/*1.在Golang中,程序遇到defer关键字,不会立即执行defer后的语句,而是将defer后的语句压入一个栈中,然后继续执行函数后面的代码;2.将defer语句压入栈中的同时,也会将相关的值同时拷贝到栈中,不会随着函数后面的变化而变化;3.栈的特点: 先进后出;4.在函数执行完毕以后,从栈中取出语句开始执行,按照先进后出的的规则执行语句;*/defer fmt.Printf("a = %d\n", a)defer fmt.Printf("b = %d\n", b)// 此处我们将a和b的值进行修改,但是并不会影响到defer语句中里的a和b变量对应的值哟~a += 100b += 200sum = a + bfmt.Printf("a = %d, b = %d, sum = %d\n", a, b, sum)return sum}func main() {fmt.Println(getSum(10, 20))
}
3 defer面试题
3.1 观察代码手写运算结果1
package mainimport ("fmt"
)func f1() int {x := 100defer func() {x++fmt.Println("in f1 x = ", x)}()return x
}func f2() (x int) {defer func() {x++fmt.Println("in f2 x = ", x)}()return 200
}func f3() (y int) {x := 300defer func() {x++fmt.Println("in f3 x = ", x)}()return x
}func f4() (x int) {defer func(x int) {x++fmt.Println("in f4 x = ", x)}(x)return 400
}func main() {fmt.Println(f1())fmt.Println(f2())fmt.Println(f3())fmt.Println(f4())
}
3.2 观察代码手写运算结果2
package mainimport ("fmt"
)func calc(index string, a, b int) int {ret := a + bfmt.Println(index, a, b, ret)return ret
}func main() {x := 1y := 2defer calc("AA", x, calc("A", x, y))x = 10defer calc("BB", x, calc("B", x, y))y = 20
}