Article
Golang 基础 3
闭包案例 -> 函数 makeCounter
package main
import "fmt"
func main() {
makeCounter := func() func() int {
count := 0
return func() int {
count++
return count
}
}
counter := makeCounter()
fmt.Println(counter())
fmt.Println(counter())
}

makeCounter是一个外层函数,内部有一个匿名函数,它的返回值是count,而makeCounter本身的返回值正是这个匿名函数。
当匿名函数开始执行时,makeCounter销毁,那么count本应该销毁,但是却在内部函数里调用,于是就形成了闭包。
总的来说,闭包就是:闭包 = 函数 + 它引用的外部变量。
延迟调用
当有多个 defer 描述的函数时,就会像栈一样先进后出的顺序执行。
package main
import "fmt"
func main() {
fmt.Println(0)
Do()
}
func Do() {
defer fmt.Println(1)
fmt.Println(2)
defer fmt.Println(3)
defer fmt.Println(4)
fmt.Println(5)
}

延迟调用通常用于释放文件资源,关闭网络连接等操作,还有一个用法是捕获panic,不过这是错误处理一节中才会涉及到的东西。
defer不推荐用于循环当中。因为没创建一个defer,都要在当前的协程里创建一片内存空间。
如果循环过程涉及的不是简单的过程,而是一个较为复杂的数据处理流程时,当外部的请求数激增时,那么在短时间内就会创建大量的defer,在循环次数很大的循环当中,就会导致占用内存激增,也就是内存泄漏。
defer和闭包可以扯上关系,如下列展示:
func main() {
var a, b int
a = 1
b = 2
defer fmt.Println(sum(a, b))
a = 3
b = 4
}
func sum(a, b int) int {
return a + b
}
这是用defer实现的,结果输出为3而不是7。
所以我们可以知道:defer捕获的是只拷贝,也就是看到声明时的参数值。
func main() {
var a, b int
a = 1
b = 2
f := func() {
fmt.Println(sum(a, b))
}
a = 3
b = 4
f()
}
使用闭包组合,结果输出就是7,因为闭包捕获的是a和b的变量本身,也就是类似指针的效果,那么闭包就会去实时查找a和 b,像一个实时监控摄像头一样,调用时采取看此刻的a和b是多少。
options模式
这是一个结构体。
type Person struct {
Name string
Age int
Address string
Salary float64
Birthday string
}
我们可以采用类似映射的方法来构造一个结构体。
xiaooming := Person{
Name: "xiaoming",
Age: 18,
Address: "地球",
Salary: 10000000,
Birthday: "13月32日",
}
另外,我们用PersonOptions类型来构造函数。
type PersonOptions func(p *Person)
func WithName(name string) PersonOptions {
return func(p *Person) {
p.Name = name
}
}
func WithAge(age int) PersonOptions {
return func(p *Person) {
p.Age = age
}
}
func WithAddress(address string) PersonOptions {
return func(p *Person) {
p.Address = address
}
}
func WithSalary(salary float64) PersonOptions {
return func(p *Person) {
p.Salary = salary
}
}
func NewPerson(options ...PersonOptions) *Person {
// 优先应用options
p := &Person{}
for _, option := range options {
option(p)
}
// 默认值处理
if p.Age < 0 {
p.Age = 0
}
return p
}
这样,对于不同实例化的需求,只需要一个构造函数就可以完成:
func main() {
pl := NewPerson(
WithName("John Doe"),
WithAge(25),
WithAddress("123 Main St"),
WithSalary(10000.00),
)
p2 := NewPerson(
WithName("Mike jane"),
WithAge(30),
)
}