什么是匿名函数

匿名函数就是没有名字的函数

Anonymous functions are functions without a name. They are also known as Function literals.

匿名函数介绍

您可以使用常用的 func 关键字定义匿名函数,但语法略有不同。首先提醒一下,以下是常规函数的语法:

  1. func FunctionName() {
  2. ...
  3. }

现在这里是匿名函数语法的样子:

  1. func() {
  2. ...
  3. }()

请注意,我们在末尾有一对额外的圆括号。这些用于传递参数;我们稍后会介绍。但是现在,这是一个基本示例:

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("Start")
  5. func() {
  6. fmt.Println("hello world!")
  7. }()
  8. fmt.Println("End")
  9. }

执行 go run demo.go

  1. learn-go go run demo.go
  2. Start
  3. hello world!
  4. End

第 10 行:当 Go 沿着父函数的代码块运行时,匿名函数被执行。还要注意这个匿名函数是在另一个函数中定义的,稍后会详细介绍。

现在让我们来看看那些圆括号是做什么用的。第一对圆括号用于设置输入参数:

  1. func(input-parameters){
  2. ...
  3. }(argument)

然后我们可以使用第二对圆括号将参数传递给函数。

匿名函数 - 图1

这个匿名函数在程序运行期间只执行一次。但是,您可以通过将匿名函数保存到变量来使匿名函数可重用,然后您可以使用该变量重复调用该函数。这是一个示例,我们将匿名函数保存到名为“greetings”的变量中(第 10 行)。

匿名函数 - 图2

第 12 行:将函数文字保存到变量时,我们必须省略尾部的圆括号。这些括号仍在使用,但在其他地方(第 16 和 17 行)

Line 12: We have to omit the trailing round brackets when saving a function literal to a variable. Those brackets are still being used but elsewhere (lines 16 and 17)

第 14 行:这表明我们的变量包含一个函数,其函数签名为 - 一个字符串输入参数和零个输出参数。

Line 14: This shows that our variable contains a function with the function signature of - one string input parameter and zero output parameters.

设置输出参数

匿名函数可以有输出参数。这些输出参数的设置和工作方式与常规功能相同。

匿名函数 - 图3

闭包函数

Closure functions are Anonymous functions that use variables from its parent function. Here’s an example of this action.

匿名函数 - 图4

Line 7: We created an integer variable, n, with an initial value of 5.

Lines 8–10: We defined an anonymous function and saved to a variable called counter

Line 11: We called the anonymous function using its variable name.

Line 13: This outputs 15. The interesting thing to note here is that the value 15 was set inside the anonymous function, but that change persisted to the parent level. In other words, Closer functions can access and change their parent’s variable.

匿名函数使用方式

方式1

在定义匿名函数时就直接使用,这种方式匿名函数只能调用一次

  1. func main() {
  2. res1 := func(n1 int, n2 int) int {
  3. return n1 + n2
  4. }(10, 20)
  5. fmt.Println(res1) //30
  6. }

方式2

将匿名函数赋给一个变量(函数变量), 再通过该变量来调用匿名函数

  1. func main() {
  2. a := func(n1 int, n2 int) int {
  3. return n1 + n2
  4. }
  5. res1 := a(10, 20)
  6. fmt.Println(res1) //30
  7. }

全局匿名函数

如果把一个匿名函数赋值给一个全局变量,则这个匿名函数在整个程序内有效

  1. var (
  2. Fun1 = func(n1 int, n2 int) int {
  3. return n1 + n2
  4. }
  5. )
  6. func main() {
  7. res1 := Fun1(10, 20)
  8. fmt.Println(res1) //30
  9. }

匿名函数用作回调函数

下面的代码实现对切片的遍历操作,遍历中访问每个元素的操作使用匿名函数来实现,用户传入不同的匿名函数体可以实现对元素不同的遍历操作,代码如下:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. // 遍历切片的每个元素, 通过给定函数进行元素访问
  6. func visit(list []int, f func(int)) {
  7. for _, v := range list {
  8. f(v)
  9. }
  10. }
  11. func main() {
  12. // 使用匿名函数打印切片内容
  13. visit([]int{1, 2, 3, 4}, func(v int) {
  14. fmt.Println(v)
  15. })
  16. }

代码说明如下:

  • 第 8 行,使用 visit()函数将整个遍历过程进行封装,当要获取遍历期间的切片值时,只需要给 visit() 传入一个回调参数即可。
  • 第 18 行,准备一个整型切片 []int{1,2,3,4} 传入 visit() 函数作为遍历的数据。
  • 第 19~20 行,定义了一个匿名函数,作用是将遍历的每个值打印出来。

常规函数VS匿名函数

在上面的例子中,我们已经看到我们可以使用variableName(argument)来调用一个函数。这在语法上类似于调用常规函数。所以您可能想知道,基于变量的匿名函数与常规函数有何不同?这里总结了一些区别:

  1. 匿名函数只能在其他函数中定义。
  2. 匿名函数只能从其父函数的主体中调用。
  3. 匿名函数可以从其父函数访问变量,即闭包函数。

所以回到前面的例子,我们可以从主函数内部调用我们的匿名函数,例如第 11 行。

什么时候应该使用匿名函数?

一种可能的情况是您正在编写的函数不断增长,直到它变成“怪物函数”。

发生这种情况时,您的第一反应可能是将您的怪物函数拆分为一个不那么令人讨厌的怪物函数和几个较小的常规函数。但这意味着您项目中的其他代码也可以调用那些您可能不想要的较小函数。

因此,如果您想将您的怪物函数分解为更小的子函数,那么您可以将子函数创建为匿名函数。这样,只有您的怪物功能才能唯一访问这些子功能