Skip to content

Commit 3c1d8fb

Browse files
committed
docs: update docs
1 parent 954fc71 commit 3c1d8fb

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
order: 50
3+
title: Closure
4+
icon: line-md:sun-rising-twotone-loop
5+
head:
6+
- - meta
7+
- name: keywords
8+
content: Go, Golang, closure, function factory, iterator
9+
---
10+
11+
## What Is a Closure?
12+
13+
::: info What Is a Closure?
14+
A closure is an entity composed of a function and the associated referencing environment. In simple terms, a closure is a function that references variables outside its own scope. The lifetime of this function can extend beyond the scope in which it was created.
15+
16+
Example
17+
18+
```go
19+
package main
20+
21+
import "fmt"
22+
23+
func main() {
24+
count := func() func() int {
25+
i := 0 // Initialize a local variable within the function
26+
return func() int {
27+
i++ // Increment the local variable
28+
return i
29+
}
30+
}()
31+
32+
fmt.Println(count())
33+
fmt.Println(count())
34+
}
35+
36+
// Output
37+
// 1
38+
// 2
39+
```
40+
41+
Notice that `i` is a local variable within the `count` function. Executing the function twice might lead one to expect the output to be `1` both times. However, the actual output is `1` followed by `2`. The reason is that when assigning the closure to a variable (`count`), it retains a pointer to `i`. As a result, `i` is preserved beyond the function's execution due to escape analysis. **If the function is not assigned to a variable, executing it multiple times will yield consistent results.**
42+
:::
43+
44+
## Use Cases
45+
46+
### Middleware
47+
48+
When defining web middleware, we often encounter code like the following:
49+
50+
```go
51+
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
52+
return func(w http.ResponseWriter, r *http.Request) {
53+
m := validPath.FindStringSubmatch(r.URL.Path)
54+
if m == nil {
55+
http.NotFound(w, r)
56+
return
57+
}
58+
fn(w, r, m[2]) // If no issues, continue executing fn
59+
}
60+
}
61+
```
62+
63+
In this example, we return an `http.HandlerFunc` that calls the `fn` function. This allows us to achieve chainable operations—executing middleware code while still continuing with the main function.
64+
65+
## State Sharing
66+
67+
Closures can be used to share state across multiple invocations of a function. A common example is an iterator:
68+
69+
```go
70+
package main
71+
72+
import "fmt"
73+
74+
func main() {
75+
num := []int{1, 2, 3, 4}
76+
77+
iterator := func(arr []int) func([]int) (int, bool) {
78+
i := -1
79+
return func(arr []int) (int, bool) {
80+
i++
81+
if i < len(arr) {
82+
return arr[i], true
83+
}
84+
return 0, false
85+
}
86+
}
87+
88+
iter := iterator(num)
89+
90+
for {
91+
value, ok := iter(num)
92+
if !ok {
93+
return
94+
}
95+
96+
fmt.Println(value)
97+
}
98+
}
99+
100+
// Output
101+
// 1
102+
// 2
103+
// 3
104+
// 4
105+
```
106+
107+
## Callback Functions
108+
109+
We can also pass callback functions as parameters:
110+
111+
```go
112+
func GetData(data int, callback func(int)) {
113+
go func() {
114+
result := data + 2
115+
callback(result)
116+
}()
117+
}
118+
```
119+
120+
In the above example, after passing in `data`, the `callback` can access `result` for additional callback operations.
121+
122+
## Function Factories
123+
124+
Closures allow us to create function factories by returning functions based on input parameters:
125+
126+
```go
127+
func CalculationFactory(operation string) func(int, int) int {
128+
switch operation {
129+
case "add":
130+
return func(a, b int) int {
131+
return a + b
132+
}
133+
case "subtract":
134+
return func(a, b int) int {
135+
return a - b
136+
}
137+
case "multiply":
138+
return func(a, b int) int {
139+
return a * b
140+
}
141+
case "divide":
142+
return func(a, b int) int {
143+
if b != 0 {
144+
return a / b
145+
}
146+
return 0
147+
}
148+
default:
149+
return nil
150+
}
151+
}
152+
```
153+
154+
By passing in `"add"`, you can obtain an addition function, and by passing in `"divide"`, you can obtain a division function.
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
order: 50
3+
title: 闭包
4+
icon: line-md:sun-rising-twotone-loop
5+
head:
6+
- - meta
7+
- name: keywords
8+
content: Go, Golang, closure, 闭包, 函数工厂, 迭代器, 中间件, 回调函数
9+
---
10+
11+
## 什么是闭包?
12+
13+
::: info 什么是闭包?
14+
闭包是由函数和与其相关的引用环境组合而成的实体。简单来说,闭包就是一个引用了作用域之外的变量的函数(Func),该函数的存在时间可以超过创建他的作用域。
15+
16+
例子
17+
18+
```go
19+
package main
20+
21+
import "fmt"
22+
23+
func main() {
24+
count := func() func() int {
25+
i := 0 // 初始化函数内变量
26+
return func() int {
27+
i ++ // 函数内变量加 1
28+
return i
29+
}
30+
}()
31+
32+
fmt.Println(count())
33+
fmt.Println(count())
34+
}
35+
36+
// 结果
37+
// 1
38+
// 2
39+
```
40+
41+
我们会注意到 `i``count` 的局部变量,执行两次函数感觉上应该是都输出 `1`, 实际上输出的是 `1, 2`, 原因是在赋值时 `count` 保留着对 `i` 的指针,因此 `i` 在逃逸分析后被保留,没有随着函数的执行完毕而结束。**如果函数没有赋值给变量,则执行多次结果会保持不变。**
42+
:::
43+
44+
## 使用场景
45+
46+
### 中间件
47+
48+
我们在定义 web 中间件时经常会看到以下形式的代码:
49+
50+
```go
51+
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
52+
return func(w http.ResponseWriter, r *http.Request) {
53+
m := validPath.FindStringSubmatch(r.URL.Path)
54+
if m == nil {
55+
http.NotFound(w, r)
56+
return
57+
}
58+
fn(w, r, m[2]) // 如果没问题则继续执行 fn
59+
}
60+
}
61+
```
62+
63+
可以看到, 我们返回了一个 `http.HandlerFunc`, 这个函数里面调用了 fn, 这样的话我们就可以实现链式操作,既执行了中间件代码,又可以继续执行函数,非常方便。
64+
65+
## 状态共享
66+
67+
闭包可以用来共享多次执行函数的状态, 常见的例子是迭代器:
68+
69+
```go
70+
package main
71+
72+
import "fmt"
73+
74+
func main() {
75+
num := []int{1, 2, 3, 4}
76+
77+
iterator := func(arr []int) func([]int) (int, bool) {
78+
i := -1
79+
return func(arr []int) (int, bool) {
80+
i ++
81+
if i < len(arr) {
82+
return arr[i], true
83+
}
84+
return 0, false
85+
}
86+
}
87+
88+
iter := iterator(num)
89+
90+
for {
91+
value, ok := iter(num)
92+
if !ok {
93+
return
94+
}
95+
96+
fmt.Println(value)
97+
}
98+
}
99+
100+
// 结果
101+
//1
102+
//2
103+
//3
104+
//4
105+
```
106+
107+
## 回调函数
108+
109+
我们也可以通过传参,实现传入回调函数
110+
111+
```go
112+
func GetData(data int, callback func(int)) {
113+
go func() {
114+
result := data + 2
115+
callback(result)
116+
}
117+
}
118+
```
119+
120+
上面的例子可以看到, 我们传入 `data` 后, `callback` 可以获取到 `result` 进行额外回调操作。
121+
122+
## 函数工厂
123+
124+
通过闭包我们还可以构造函数工厂,通过传入参数返回对应函数。
125+
126+
```go
127+
func CalculationFactory(operation string) func(int, int) int {
128+
switch operation {
129+
case "add":
130+
return func(a, b int) int {
131+
return a + b
132+
}
133+
case "subtract":
134+
return func(a, b int) int {
135+
return a - b
136+
}
137+
case "multiply":
138+
return func(a, b int) int {
139+
return a * b
140+
}
141+
case "divide":
142+
return func(a, b int) int {
143+
if b != 0 {
144+
return a / b
145+
}
146+
return 0
147+
}
148+
default:
149+
return nil
150+
}
151+
}
152+
```
153+
154+
我们可以传入 `add` 获取加法函数,`divide` 获取除法函数。

0 commit comments

Comments
 (0)