跳到主要内容

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("执行匿名函数")
})
}

注意事项

  • 在并发编程中使用闭包时,需要注意变量的竞态条件,必要时使用同步机制如互斥锁。
  • 匿名函数可以访问其外部作用域的变量,这可能导致变量逃逸,需要注意内存管理。