Go语言中支持函数、匿名函数和闭包,而且函数在Go语言中属于“一等公民”golang
一等(头等)函数、支持头等函数(First Class Function)的编程语言,能够把函数赋值给变量,也能够把函数做为其它函数的参数或者返回值编程
func function_name( [参数列表-能够写多个参数] ) (返回类型列表-能够返回多个类型结果]){ 函数体 }
// ageInfo 返回年龄相关的信息 func ageInfo(name string, age int) (string, int) { nextAget := age + 1 return fmt.Sprintf("%s今年%d了", name, age), nextAget }
函数的参数和返回值都是可选的,咱们能够仅仅封装一个从上到下执行的代码把它放到函数内部闭包
// sayHello 仅仅是输出一段文字 func sayHello(){ fmt.Println("你好啊明天") }
package main import "fmt" func main() { // 经过函数名加括号就能够执行函数 sayHello() } func sayHello() { // 重复的代码能够用函数封装 fmt.Println("这是重复的代码xxxxxxxxx") }
函数能够接受参数,而后函数内部的代码能够根据参数来动态计算产出不一样的结果app
package main import "fmt" func main() { // 经过函数名加括号就能够执行函数,括号内能够传入参数 result := sayHello("eson") fmt.Println(result) // 输出结果: Hello eson } func sayHello(name string) string { // 重复的代码能够用函数封装 return fmt.Sprintf("Hello %s", name) }
func sayHello(name string, add string) string { // 重复的代码能够用函数封装 return fmt.Sprintf("Hello %s", name) }
上面函数接收了两个相同类型的参数,能够经过简写来优化下编程语言
func sayHello(name, address string) string { // 重复的代码能够用函数封装 return fmt.Sprintf("Hello %s, address is :%s", name, address) }
package main import "fmt" func main() { ret := countNumber("eson", 1, 2, 3, 4, 5) fmt.Printf("%s", ret) // eson count number :[1 2 3 4 5] } // 可变长参数注意只能用在最后一个 func countNumber(name string, number ...int) string { // 重复的代码能够用函数封装 return fmt.Sprintf("%s count number :%v", name, number) }
Go语言中经过return关键字向外输出返返回值函数
package main import "fmt" func main() { result := calculate(1, 10) fmt.Printf("result is:%v", result) // result is:11 } // 单个返回值 func calculate(x, y int) int { return x + y }
package main import "fmt" func main() { sum, sub := calculate(10, 1) fmt.Printf("sum is:%v, sub is: %v\n", sum, sub) // sum is:11, sub is: 9 } // 返回多个值 func calculate(x, y int) (int, int) { // 固然这里的值能够返回不一样类型的看实际状况 return x + y, x - y }
package main import "fmt" func main() { sum, result := calculate(10, 1) fmt.Printf("sum is:%v, %v\n", sum, result) // sum is:11, calculate sum is:11 } // 返回多个值命名 func calculate(x, y int) (sum int, result string) { // 固然这里的值能够返回不一样类型的看实际状况 sum = x + y result = fmt.Sprintf("calculate sum is:%d", sum) return sum, result }
函数内部能够引用全局变量,可是函数内定义的变量只在函数内部有效优化
package main import "fmt" func main() { } // 全局变量 var num int = 18 func sum() { fmt.Printf("全局变量的值是:%d", num) // 函数内部能够直接使用全局变量 sumRet := num + 1 fmt.Printf("%v\n", sumRet) } func referSum() { // 这里想引用sum函数的sumRet的值? No 不能够函数内部的值只能在函数内部使用 }
逻辑运算块内部是一个独立的做用域url
// 全局变量 var num int = 18 func sum() { fmt.Printf("全局变量的值是:%d", num) // 函数内部能够直接使用全局变量 sumRet := num + 1 // if是逻辑运算从if开始到结束它nebula定义的值只能在内部使用,可是它能够向上引用sumA // 函数内部是一个局部做用域能够被它下层的做用域所调用 if sumRet > 1 { sumA := 10 fmt.Printf("sumA的值是:%v\n", sumA) } // fmt.Println(sumA) 可是这里函数做用域想调用本身包含的子做用域代码块是不能够的 }
循环逻辑内部也是一个独立的做用域,而且能够向上引用它上层的做用域指针
// 全局变量 var num int = 18 func sum() { fmt.Printf("全局变量的值是:%d", num) // 函数内部能够直接使用全局变量 sumRet := num + 1 for i := 0; i < sumRet; i++ { // 这个i每次循环的时候都会被赋值新的值 // 在循环内定义的值只有在本次循环内有效 cycle := i + num fmt.Printf("i is:%d\n", cycle) } }
什么状况下用值传递?code
package main import "fmt" type person struct { name string age int } func main() { p1 := person{name: "John", age: 18} showInfo(p1) } // 只想展现的时候能够传值 func showInfo(student person) { fmt.Printf("学生:%s的年龄是:%d", student.name, student.age) }
什么状况下用指针传递?
package main import "fmt" type person struct { name string age int } func main() { p1 := person{name: "John", age: 18} // 过了1年年龄加1 happyNewYear(&p1) fmt.Printf("name:%s, age is:%d", p1.name, p1.age) } func happyNewYear(p *person) { p.age++ }
固然结构体能够直接使用指针变量
package main import "fmt" type person struct { name string age int } func main() { p1 := &person{name: "John", age: 18} // 初始化一个person的结构体指针变量 // 过了1年年龄加1 happyNewYear(p1) fmt.Printf("name:%s, age is:%d", p1.name, p1.age) } func happyNewYear(p *person) { p.age++ }
package main import "fmt" func main() { // sum 这个函数的类型 fmt.Printf("%T", sum) // func(int, int) int } func sum(x, y int) int { return x + y }
从上面能够看出来每一个函数都是有一个类型的,那咱们在函数里传函数的时候就能够这么写
package main import "fmt" func main() { // sum 这个函数的类型 fmt.Printf("%T\n", sum) // func(int, int) int fmt.Printf("%v\n", calculate(100, 200, sum)) // 300 } func sum(x, y int) int { return x + y } // 定义了一个计算函数,它接收了3个参数 // x,y int类型 // op 是一个函数类型 func(x,y int) int func calculate(x, y int, op func(x, y int) int) int { return op(x, y) }
package main import "fmt" func main() { // sum 这个函数的类型 fmt.Printf("%T\n", sum) // func(int, int) int fmt.Printf("%v\n", calculate(100, 200, sum)) // 300 } func sum(x, y int) int { return x + y } // 定义一个函数类型并使用它 type fType func(int, int) int // 定义了一个计算函数,它接收了3个参数 // x,y int类型 // op 是一个函数类型 ftype func calculate(x, y int, op fType) int { return op(x, y) }
package main import ( "errors" "fmt" ) func main() { a, b := 100, 200 var method string // 获取用户输入 _, _ = fmt.Scanln(&method) // 获取放方法 job, err := calculate(method) // 执行 if err != nil { fmt.Printf("%v\n", err) } else { fmt.Printf("%v\n", job(a, b)) } } func sum(x, y int) int { return x + y } func sub(x, y int) int { return x - y } // 定义一个函数类型并使用它 type fType func(int, int) int func calculate(op string) (fType, error) { switch op { case "+": return sum, nil case "-": return sub, nil default: return nil, errors.New("没法识别的方法") } }
当咱们须要临时在函数内使用一个函数的时候不能像定义普通函数那样使用了,这个时候就用到了匿名函数(没有名称的函数就是匿名函数)
// 匿名函数没有名称 func(参数)(返回值){ 函数体 }
package main import "fmt" func main() { // 匿名函数赋值变量并赋值 sum := func(x, y int) int { return x + y } fmt.Printf("%v\n", sum(100, 200)) }
package main import "fmt" func main() { // 定义一个匿名变量后面跟扩韩直接传值并运行 func(x, y int) { fmt.Printf("sum : %d + %d result is :%d\n", x, y, x+y) }(100, 200) // 结果: sum : 100 + 200 result is :300 }
匿名函数多用于实现回调函数和闭包
闭包就是:
package main import ( "errors" "fmt" ) // 这个函数返回一个func(int)类型的函数 func wapper() func(arg int) { x := 0 return func(y int) { x += y fmt.Println(x) } } func main() { // 当这个时候外部函数销毁了,可是x这个变量并无,由于它被内部函数所引用了 f := wapper() f(10) f(10) f(10) } // 结果: 10 20 30
简单例子
package main import ( "fmt" "time" ) func main() { // 经过timed函数能够获取函数的运行时间 timed(run)() } // 这个函数接收一个函数,并返回一个函数 func timed(f func()) func() { return func() { start := time.Now() f() fmt.Printf("这个函数运行须要:%dms\n", time.Since(start)/1000000) } } func run() { time.Sleep(time.Second * 3) }
实际应用例子
package main import ( "fmt" "net/http" "time" ) func main() { http.HandleFunc("/hello", timed(hello)) http.ListenAndServe(":3000", nil) } func timed(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() f(w, r) end := time.Now() fmt.Println("The request took", end.Sub(start)) } } func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "<h1>Hello!</h1>") }
正常来讲要在函数内使用一个变量,要不就定义一个全局变量,在函数内可使用,或者经过闭包的方式调用
简单例子
package main import ( "errors" "fmt" ) // 这个函数返回一个func(int)类型的函数 func wapper() func(arg int) { x := 0 return func(y int) { x += y fmt.Println(x) } } func main() { // 当这个时候外部函数销毁了,可是x这个变量并无,由于它被内部函数所引用了 f := wapper() f(10) f(10) f(10) } // 结果: 10 20 30
应用例子
package main import ( "fmt" "net/http" ) type Database struct { Url string } func NewDatabase(url string) Database { return Database{url} } func main() { db := NewDatabase("localhost:5432") http.HandleFunc("/hello", hello(db)) http.ListenAndServe(":3000", nil) } func hello(db Database) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, db.Url) } }
defer是go语言里的延迟函数,它定义在一个函数内defer后面跟的表达式将会延迟执行
package main import ( "fmt" "time" ) func main() { runTasks() } func runTasks() int { start := time.Now() // 直接在这里定义一个延迟函数 // 用来统计这个函数执行了多久 defer func(stime time.Time) { fmt.Printf("这个函数运行须要:%dms\n", time.Since(start)/1000000) }(start) // 开始运行函数逻辑 fmt.Println("函数内逻辑执行1") time.Sleep(time.Second * 2) fmt.Println("函数内逻辑执行2") return 666 // defer fmt.Println("若是在return语句后面加延迟语句是不能够的") }
golang目前没有像其余语言异常处理相似:try catch,可是能够经过defer来捕获 异常举例来讲!~
在go函数内经过panic来触发异常并退出程序,这里须要注意不要滥用:panic,panic会让整个程序挂掉
若是一个函数出现了一个未知的异常后,它的处理逻辑是根据调用链,不断向上返回直到碰到recover函数
package main import ( "fmt" ) func main() { f1() } func f1() { defer func() { if err := recover(); err != nil { fmt.Printf("f1函数捕获到异常了,异常报错是:%v\n", err) } }() fmt.Println("这个是第1层函数") f2() } func f2() { panic("f2 触发了panic") fmt.Println("这个是第2层函数") // panic后这里是不执行的 }
总结下