侧边栏壁纸
博主头像
梦幻世界博主等级

行动起来,活在当下

  • 累计撰写 23 篇文章
  • 累计创建 2 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

DAY09:init函数;匿名函数;闭包;defer;函数参数传递;变量作用域

梦幻世界
2024-05-24 / 0 评论 / 0 点赞 / 64 阅读 / 8593 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-05-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

init函数

基本介绍

每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用,也就是说 init 会在 main 函数前被调用。

使用案例

package main

import "fmt"

func init() {
	fmt.Println("init()执行")
}

func main() {
	fmt.Println("main()执行")
}

执行结果

注意事项

  1. 如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程全局变量定义->init 函数->main 函数

  2. init 函数最主要的作用,就是完成一些初始化的工作

  3. 细节说明: 面试题:案例如果 main.go 和 utils.go 都含有 变量定义,init 函数时,执行的流程又是怎么样的呢?

package main

import "fmt"

var age int = test()

func test() int {
	fmt.Println("test()执行")
	return 40
}

func init() {
	fmt.Println("init()执行")
}

func main() {
	fmt.Println("main()执行")
}

关于注意点3的说明

匿名函数

基本介绍

Go 支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。

使用案例1

在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

package main

import "fmt"

func main() {
	res := func(n1 int, n2 int) int {
		return n1 + n2
	}(10, 10)

	fmt.Println("res =", res)
}

使用案例2

将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数

package main

import "fmt"

func main() {
	f := func(n1 int, n2 int) int {
		return n1 + n2
	}
	res := f(20, 10)
	fmt.Println("res =", res)
}

使用案例3

如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序有效。

package main

import "fmt"

var (
	Fun1 = func(n1 int, n2 int) int {
		return n1 * n2
	}
)

func main() {
	res := Fun1(3, 9)
	fmt.Println("res =", res)
}

闭包

基本介绍

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

基本语法

package main

import "fmt"

func AddUpper() func(int) int {
	var n = 10
	return func(i int) int {
		n += i
		return n
	}
}

func main() {
	f := AddUpper()
	fmt.Println("n1 = ", f(1))
	fmt.Println("n2 = ", f(2))
	fmt.Println("n3 = ", f(3))
}

说明及注意事项

  1. AddUpper 是一个函数,返回的数据类型是 fun (int) int

  2. 闭包的说明:返回的是一个匿名函数, 但是这个匿名函数引用到函数外的n ,因此这个匿名函数就和n 形成一个整体,构成闭包。

  3. 当我们反复的调用 f 函数时,因为 n 是初始化一次,因此每调用一次就进行累计。

  4. 闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包。

练习

编写一个函数 makeSuffix(suffix string) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包,调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg 后缀,则返回原文件名。

package main

import (
	"fmt"
	"strings"
)

func makeSuffix(suffix string) func(name string) string {
	return func(name string) string {
		if strings.HasSuffix(name, suffix) {
			return name
		} else {
			return name + suffix
		}
	}
}

func main() {
	f := makeSuffix(".jpg")
	fmt.Println("文件名1 = ", f("file1"))
	fmt.Println("文件名2 = ", f("file2.jpg"))
}

defer

基本介绍

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。

使用案例

package main

import "fmt"

func getSum(n1 int, n2 int) int {
	// 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句压入到一个栈中
	// 当函数执行完毕后,在从 defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制)
	defer fmt.Println("n1 =", n1)
	defer fmt.Println("n2 =", n2)

	fmt.Println("getSum()执行到defer语句后面了")
	return n1 + n2
}

func main() {
	num := getSum(1, 2)
	fmt.Println("num =", num)
}

注意事项

  1. 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句压入到一个栈中

  2. 当函数执行完毕后,在从 defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制)

  3. 在 defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。

package main

import "fmt"

func getSum(n1 int, n2 int) int {
	defer fmt.Println("n1 =", n1) // 这个把值进行拷贝,不会进行+1的操作,是独立的一个栈空间
	defer fmt.Println("n2 =", n2)

	n1++
	n2++
	fmt.Println("getSum()执行到defer语句后面了")
	return n1 + n2
}

func main() {
	num := getSum(1, 2)
	fmt.Println("num =", num)
}

函数参数的传递方式

传递方式

  1. 值传递

  2. 引用传递

其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低。

值类型和引用类型

  1. 值类型:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体struct

  2. 引用类型:指针、slice 切片、map、管道chan、interface 等都是引用类型

值传递和引用传递使用特点

值传递和引用传递

如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

变量作用域

  1. 函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部

  2. 函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序有效

  3. 如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块

package main

import "fmt"

var name = "tom"

func test1() {
	fmt.Println("name =", name)
}

func test2() { // 编译器使用就近原则
	name := "jack"
	fmt.Println("name =", name)
}

func main() {
	fmt.Println("name =", name)
	test1()
	test2()
	test1()
}

0

评论区