Go 函数的基础
函数签名
在 Go 语言中,函数签名指的是函数的定义,包括函数的名称、参数列表和返回值类型。每个函数都有一个类型签名,明确规定了它可以接受的参数和返回的值。
多返回值
Go 的函数可以返回多个值,这使得处理错误或返回多个结果更加方便。
func divide(dividend, divisor float64) (float64, error) {
if divisor == 0.0 {
return 0.0, fmt.Errorf("无法除以零")
}
return dividend / divisor, nil
}
命名返回值
在函数定义中,可以为返回值命名,这样可以在函数体内直接对返回值变量进行赋值,最后使用 return
返回。
func sumAndDiff(a, b int) (sum int, diff int) {
sum = a + b
diff = a - b
return
}
首字母大写的导出
如果在命名函数时首字母大写,该函数就可以被其他包访问。如果首字母小写,则仅在当前包内可见。
函数是一等公民
在 Go 语言中,函数是一等公民,意味着函数可以作为参数传递,也可以作为其他函数的返回值。
匿名函数
匿名函数是没有名字的函数,可以在代码中定义并立即使用,或者赋值给变量以供后续调用。
func main() {
// 将匿名函数赋值给变量
square := func(x int) int {
return x * x
}
fmt.Println(square(4)) // 输出 16
// 直接执行匿名函数
func(message string) {
fmt.Println(message)
}("Hello, World!")
}
方法和函数的区别
在 Go 中,方法是附加到特定类型的函数。定义方法时,需要在函数名前指定接收者类型。
type Circle struct {
Radius float64
}
// 为 Circle 类型定义方法 Area
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
circle := Circle{Radius: 5}
fmt.Println(circle.Area()) // 调用 Circle 类型的方法 Area
}
无需前置声明
在 Go 语言中,函数可以在使用之前定义,无需像 C 语言一样先声明后使用。
JavaScript 的作用域
在 JavaScript 中,作用域链由函数作用域和词法作用域组成,决定了如何在程序中查找变量。函数可以创建新的作用域,并且可以嵌套在其他函数内部。
词法作用域和函数作用域
词法作用域是指作用域在代码编写时决定,而不是在代码执行时决定。这意味着函数的作用域由它在源代码中的位置确定。
函数作用域意味着每个函数都创建了一个新的作用域,在该作用域中定义的变量和函数只能在该作用域内部访问。
作用域链的形成
当一个函数嵌套在另一个函数中时,会形成作用域链。内部函数的作用域链包括:
- 自己的作用域
- 外部函数的作用域
- 全局作用域
Go 的作用域
在 Go 语言中,作用域同样适用于变量的可见性和生命周期。
块级作用域
在花括号 {}
包围的代码块内声明的变量,只能在该代码块内访问。
函数作用域
函数的参数和在函数内声明的变量,具有函数作用域,只能在函数内部访问。
包级作用域
在函数外部、包内部声明的变量,具有包级作用域,可以在同一包内的所有文件中访问。
在函数内部定义函数
在 Go 语言中,可以在函数内部定义匿名函数,但不能定义具名的嵌套函数。如果需要在函数内部定义函数,可以使用匿名函数并赋值给变量,或者立即执行。
直接定义并执行匿名函数
func main() {
fmt.Println("外部函数")
func() {
fmt.Println("内部匿名函数")
}() // 定义并立即执行匿名函数
}
将匿名函数赋值给变量
func main() {
innerFunc := func() {
fmt.Println("内部匿名函数")
}
innerFunc() // 调用匿名函数
}
匿名函数作为返回值
func getGreeter(name string) func() {
return func() {
fmt.Printf("Hello, %s!\n", name)
}
}
func main() {
greeter := getGreeter("sumingcheng")
greeter() // 输出: Hello, sumingcheng!
}
匿名函数作为参数
func execute(f func()) {
f() // 执行传入的函数
}
func main() {
execute(func() {
fmt.Println("执行匿名函数")
})
}
注意事项
- 在并发编程中使用闭包时,需要注意变量的竞态条件,必要时使用同步机制如互斥锁。
- 匿名函数可以访问其外部作用域的变量,这可能导致变量逃逸,需要注意内存管理。