go

回调函数,匿名函数和闭包

概念

  • 回调函数:是指函数作为参数传递到另一个函数中,然后在另一个函数中被调用。可以接受一个或多个函数作为输入。
  • 匿名函数:是没有函数名的函数。由一个不带函数名的函数声明和函数体组成。匿名函数可以赋值给一个变量或者直接执行。
  • 闭包:当匿名函数引用了外部作用域中的变量时就成了闭包。使用闭包的意义主要就是缩小变量作用域,减少对全局变量的污染。

回调函数

下面是几个示例:

加法运算

package main

import "fmt"

func Add(a, b int) {
    fmt.Printf("%d + %d = %d\n", a, b, a+b)
}

// 接受一个整数 y 和一个函数 f 作为参数
func callback(y int, f func(int, int)) {
    // 在函数内部,调用函数 f,并将 y 和整数值 2 作为参数传递给 f
    f(y, 2)
}

// sumNum 接受一个整数切片 data 和一个回调函数 call 作为参数
func sumNum(data []int, call func(int)) {
    sum := 0
    for _, v := range data {
        sum += v
    }
    // 将总和作为参数传递给回调函数 call
    call(sum)
}
func main() {
    // 将 1 和函数 Add 作为参数,Add(1, 2)
    callback(1, Add)

    sumNum([]int{1, 2, 3}, func(num int) {
        fmt.Println("sum:", num)
    })

    sumNum([]int{1, 2, 3}, func(num int) {
        if num < 10 {
            fmt.Println("sum < 10")
        } else {
            fmt.Println("sum > 10")
        }
    })
}

事件处理

package main

import (
    "fmt"
    "time"
)

type Button struct {
    // ClickedHandler 用于保存按钮被点击时要执行的函数
    ClickedHandler func()
}

// Click 该方法接受一个函数作为参数,并将其赋值给 ClickedHandler 字段
func (b *Button) Click(handler func()) {
    b.ClickedHandler = handler
}

func main() {
    button := &Button{}

    // 传递一个匿名函数作为参数,匿名函数定义了按钮被点击时要执行的逻辑
    button.Click(func() {
        fmt.Println("按钮被点击了")
    })
    // 模拟等待用户点击按钮的时间
    time.Sleep(2 * time.Second)
    // 检查 button.ClickedHandler 字段是否为 nil,来判断是否有回调函数注册
    if button.ClickedHandler != nil {
        // 如果有回调函数注册,调用 button.ClickedHandler() 来触发回调函数的执行
        button.ClickedHandler()
    }
}

sort排序

func Slice(x any, less func(i int, j int) bool)
func SliceStable(x any, less func(i int, j int) bool)
// Slice/SliceStable使用less函数对x进行排序。x必须是切片。
package main

import (
    "fmt"
    "sort"
)

func main() {
    numbers := []int{3, 5, 2, 1, 4}

    sort.SliceStable(numbers, func(i, j int) bool {
        return numbers[i] < numbers[j]
    })

    fmt.Println(numbers)
}

[1 2 3 4 5]

异步处理

package main

import (
    "fmt"
    "io"
    "net/http"
)

// getData 函数接受一个URL和一个回调函数作为参数
func getData(url string, callback func(data []byte, err error)) {
    // 异步发起http请求
    go func() {
        res, err := http.Get(url)
        if err != nil {
            callback(nil, err)
            return
        }
        defer res.Body.Close()

        data, err := io.ReadAll(res.Body)
        if err != nil {
            callback(nil, err)
            return
        }

        callback(data, nil)
    }()
}

func main() {
    url := "http://www.baidu.com"

    getData(url, func(data []byte, err error) {
        if err != nil {
            fmt.Println("数据获取失败:", err)
            return
        }
        fmt.Println("数据获取成功:", string(data))
    })
}

匿名函数

使用情况分为三种:赋值给一个变量,直接执行,作为参数即上面的回调函数。

赋值给一个变量

package main

import "fmt"

func main() {
    sumFun := func(x, y int) int {
        return x + y
    }
    sum := sumFun(1, 2)
    fmt.Println(sum)

    f := func() {
        fmt.Println("anonymous function")
    }
    f()
    fmt.Println(f)
    fmt.Printf("%T\n", f)
}

直接执行

package main

import "fmt"

func main() {
    func() {
        fmt.Println("Hello World!")
    }()

    func(str string) {
        fmt.Println("Hello " + str)
    }("World!")
}

闭包

闭包引用了函数体之外的变量,这个变量叫自由变量。匿名函数可以对这个引用的变量进行访问和赋值,即使这些变量已经超出了其作用域的生命周期。

Golang中所有的匿名函数都是闭包。

实现:在函数内部定义局部变量,把另一个函数当作返回值,局部变量对于匿名函数就相当于全局变量,所以多次调用匿名函数,局部变量的值也跟随变化。

需要注意的是,闭包可能会导致内存泄漏问题。因为闭包会持有对外部变量的引用,如果闭包的生命周期比外部变量的生命周期长,那么这些外部变量将无法被垃圾回收,从而导致内存泄漏。

计数器

package main

import "fmt"

// Count 函数不接受任何参数,但返回一个函数。
func Count() func(int) int {
    a := 0
    // 返回一个匿名函数
    return func(b int) int {
        a += b
        return a
    }
}

func main() {
    // 调用 Count 函数并将返回的函数赋值给变量 count
    count := Count()
    fmt.Println(count(1)) // 1
    fmt.Println(count(1)) // 2
    // 重新调用 Count() 会重新声明及赋值局部变量 a
    count2 := Count()
    fmt.Println(count(1)) // 1
}

值传递

package main

import "fmt"

func main() {
    i := 0
    // 闭包
    defer func() {
        fmt.Println("闭包:", i)
    }()
    // 非闭包
    defer fmt.Println("非闭包:", i)

    i = 10
}

非闭包: 0
闭包: 10
分类: go
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x