函数声明
函数声明包括函数名、形参、返回值和函数体
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3,4)) // "5"
复制代码
- 形式参数列表描述了函数的参数名以及参数类型。这些参数作为局部变量,其值由参数调用者提供。
- 返回值列表描述了函数返回值的变量名以及类型。如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。
如果一组形参或返回值有相同的类型,我们不必为每个形参都写出参数类型。
func f(i, j, k int, s, t string) { /* ... */ }
func f(i int, j int, k int, s string, t string) { /* ... */ }
复制代码
返回值也可以像形式参数一样被命名。
- 在这种情况下,每个返回值被声明成一个局部变量,并根据该返回值的类型,将其初始化为该类型的零值。
- 如果一个函数在声明时,包含返回值列表,该函数必须以 return语句结尾,除非函数明显无法运行到结尾处。
func add(x int, y int) int {return x + y}
func sub(x, y int) (z int) { z = x - y; return}
复制代码
在函数体中,函数的形参作为局部变量,被初始化为调用者提供的值。函数的形参和有名返回值作为函数最外层的局部变量,被存储在相同的词法块中。
实参通过值的方式传递,因此函数的形参是实参的拷贝。 对形参进行修改不会影响实参。但是,如果实参包括引用类型,如指针,slice(切片)、map、function、channel等类型,实参可能会由于函数的间接引用被修改。
返回多个值
在Go中,一个函数可以返回多个值。 许多标准库中的函数返回2个值,一个是期望得到的返回值,另一个是函数出错时的错误信息。
func findLinks(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
return visit(nil, doc), nil
}
复制代码
调用多返回值函数时,返回给调用者的是一组值,调用者必须显式的将这些值分配给变量
links, err := findLinks(url)
复制代码
如果某个值不被使用,可以将其分配给blank identifier
links, _ := findLinks(url) // errors ignored
复制代码
如果一个函数所有的返回值都有显式的变量名,那么该函数的return语句可以省略操作数。这称之为bare return。
func CountWordsAndImages(url string) (words, images int, err error) {
resp, err := http.Get(url)
if err != nil {
return
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
err = fmt.Errorf("parsing HTML: %s", err)
return
}
words, images = countWordsAndImages(doc)
return
}
复制代码
按照返回值列表的次序,返回所有的返回值,在上面的例子中,每一个return语句等价于:
return words, images, err
复制代码
可变参数
参数数量可变的函数称为可变参数函数。典型的例子就是fmt.Printf和类似函数。Printf首先接收一个必备的参数,之后接收任意个数的后续参数。
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
复制代码
如果原始参数已经是切片类型,只需在最后一个参数后加上省略符。
values := []int{1, 2, 3, 4}
fmt.Println(sum(values...)) // "10"
复制代码
匿名函数
拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal),我们可绕过这一限制,在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似,区别在于func关键字后没有函数名。函数值字面量是一种表达式,它的值被称为匿名函数(anonymous function)。
strings.Map(func(r rune) rune {
return r + 1
}, "HAL-9000")
复制代码
在函数内部可以声明匿名函数,但不能声明一个正常的函数
func a(data1 int) {
fmt.Println(data1)
b := func(data2 int) {
fmt.Println(data2)
}
b(3)
}
复制代码
闭包函数
通过这种方式定义的函数可以访问完整的词法环境(感觉类似js的闭包),这意味着在函数中定义的内部函数可以引用该函数的变量,如下例所示:
// squares返回一个匿名函数。
// 该匿名函数每次被调用时都会返回下一个数的平方。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // "1"
fmt.Println(f()) // "4"
fmt.Println(f()) // "9"
fmt.Println(f()) // "16"
}
复制代码
自执行函数
func main() {
// 自执行函数
(func() {
fmt.Println("hello world")
})()
}
复制代码
Deferred函数
延迟调用,即无论在哪,加了defer的函数会最后执行
func main() {
defer def1()
fmt.Println("1")
fmt.Println("2")
defer def2()
fmt.Println("3")
}
func def1() {
fmt.Println("i am defer1")
}
func def2() {
fmt.Println("i am defer2")
}
复制代码
最后输出结果:
- 1
- 2
- 3
- i am defer2
- i am defer1




近期评论