永远不要高估自己
1. 变量作用域
1.1 全局变量
全局变量是定义在函数外部的变量,全局有效。函数体中可以访问到全局作用域的变量。
package main
import "fmt"
var num = 100
func f1(){
fmt.Println(num) // 函数内部可以访问到全局作用域的变量
}
func main(){
f1()
}
1.2 局部变量
局部变量分为两种:1.函数体内部定义的变量。2.语句块中定义的变量
-
函数内部定义的变量
func f1(){ // 定义一个局部变量(函数内部),该变量只在该函数中有效。其他地方无法访问 var x int64 = 100 fmt.Println(x) } func main(){ f1() // 100 fmt.Println(x) // 报错:undefined: x }
-
如果全局作用域与局部作用域有变量重名,优先调用自己作用域的变量。
package main import "fmt" var num int64 = 100 func f1(){ num:=99 fmt.Println(num) // 99 优先调用自己作用域的变量 } func main(){ fmt.Println(num) // 100 自己作用域没有就去上一层找变量 }
-
-
语句块中的变量,通常在
if判断
、for循环
、switch
体现func f1(x,y int){ fmt.Println(x,y)//函数的参数只能在函数内部生效 if x >= 0{ z := 100 // 变量z只能在if语句中生效 fmt.Println(z) } fmt.Println(z) // 报错,无法使用变量z } // for 循环,switch里面也是一样的
2. 高阶函数
高阶函数分为函数作为参数和函数作为返回值两部分
2.1 函数作为参数
func add(x,y int)int{
return x + y
}
func calc(x,y int,op func(int,int)int) int{
return op(x,y)
}
func main(){
ret := calc(8,9,add)
fmt.Println(ret) // 17
}
2.2 函数作为返回值
func do(s string) (func(int,int) int,error){
switch s {
case "+":
return add, nil
case "-":
return sub, nil
default:
err := errors.New("无法识别的操作符")
return nil, err
}
}
2.3 匿名函数与闭包
-
匿名函数
-
把函数作为返回值,但是在Go语言中函数内部不能定义有名函数,所以可以在函数内定义匿名函数。
-
匿名函数格式
func(参数)(返回值){ 函数体 }
-
匿名函数没有函数名,所以不能像调用普通函数那样调用,可以将匿名函数赋值给一个变量通过调用变量来执行匿名函数。
func main(){ //将函数赋值给变量 add := func(x,y int){ fmt.Println(x+y) } add(10,20) // 通过变量调用匿名函数 // 自执行函数,匿名函数定义完加()直接执行函数 func(x,y int){ fmt.Println(x+y) }(10,20) }
-
匿名函数多用于回调函数或者闭包函数
-
-
闭包函数
- 闭包指的是一个函数和与其相关的引用环境组合而成的实体
func adder() func(int) int{ var x int return func(y int) int { x += y return x } } func main(){ var f = adder() fmt.Println(f(10)) fmt.Println(f(20)) fmt.Println(f(30)) f1 := adder() fmt.Println(f1(50)) fmt.Println(f1(60)) }
-
变量
f
是一个函数并且引用了其他外部作用域的变量x
,此时f
就是一个闭包。在f
的生命周期内,变量x
也是一直有效func adder(x int)func(int) int{ return func(y int) int{ x+=y return x } } func main(){ var f = adder2(10) fmt.Println(f(10)) //20 fmt.Println(f(20)) //40 fmt.Println(f(30)) //70 f1 := adder2(20) fmt.Println(f1(40)) //60 fmt.Println(f1(50)) //110 }
2.3.1 闭包示例
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("test"))
fmt.Println(txtFunc("test"))
}
func calc(base int) (func(int) int,func(int) int){
add := func(i int) int{
base += i
return base
}
sub := func(i int) int{
base -= i
return base
}
return add,sub
}
func main(){
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2)) //11 9
fmt.Println(f1(3), f2(4)) //12 8
fmt.Println(f1(5), f2(6)) //13 7
}
3.derfer语句(延迟调用)
Go语言中的defer
语句会将后面紧跟的语句进行延迟处理。在defer
归属的函数即将返回时,延迟处理的语句按defer
定义的逆序进行执行
举个栗子
func main(){
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
// 执行结果
start
end
3
2
1
由于defer
语句延迟调用的特性,所以defer
语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
3.1 defer执行的时机
3.2 defer经典案例
func f1() int {
x := 5
defer func() {
x++
}()
return x
}
func f2() (x int) {
defer func() {
x++
}()
return 5
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
}
3.3 defer 面试题
package main
import "fmt"
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
}
4.内置函数
内置函数 | 功能介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
4.1 panic、recover
Go语言中目前(Go1.12)是没有异常机制,但是使用panic/recover
模式来处理错误。 panic
可以在任何地方引发,但recover
只有在defer
调用的函数中有效。 首先来看一个例子:
func funcA() {
fmt.Println("func A")
}
func funcB() {
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
输出:
func A
panic: panic in B
goroutine 1 [running]:
main.funcB(...)
.../code/func/main.go:12
main.main()
.../code/func/main.go:20 +0x98
程序运行期间funcB
中引发了panic
导致程序崩溃,异常退出了。这个时候我们就可以通过recover
将程序恢复回来,继续往后执行。
func funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
err := recover()
//如果程序出出现了panic错误,可以通过recover恢复过来
if err != nil {
fmt.Println("recover in B")
}
}()
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
注意:
recover()
必须搭配defer
使用。defer
一定要在可能引发panic
的语句之前定义。
funcA() {
fmt.Println("func A")
}
func funcB() {
defer func() {
err := recover()
//如果程序出出现了panic错误,可以通过recover恢复过来
if err != nil {
fmt.Println("recover in B")
}
}()
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
注意:
recover()
必须搭配defer
使用。defer
一定要在可能引发panic
的语句之前定义。