闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数A内部创建另一个函数B,通过另一个函数B访问这个函数A的局部变量。
JavaScript中的for循环的闭包有一个坑,先看代码:
function print(i){
console.log("index: ", i)
}
var arr = []
for (var index = 0; index < 3; index++) {
arr[index] = function(){
print(index)
}
}
arr.forEach(element => {
element()
})
结果如下:
index: 3
index: 3
index: 3
如果你的循环变量是用var声明的那么每次调用闭包打印的其实不是index循环中的副本而是index最后的值,也就是3
Go语言声明变量也是用var, 那么是不是也会有类似的问题出现呢?让我们来看下这段代码(这里的例子我们采用简写语法糖没有使用var, 但是结果是一样的)
for i:=0; i<3; i++ {
go func(){
fmt.Println(i)
}()
}
结果不会是0 1 2, 而且可能会出现打印3的情况, IDE也会出现警告 因为这里我们使用的是goroutine,可能有的goroutine会在循环还没结束的时候就运行了,所以和上面js的例子不同, 不一定会是3个goroutine都打印3
正确用法:
// 结果是0 1 2, 有可能是乱序的
for i:=0; i<3; i++ {
n := i
go func(){
fmt.Println(n)
}()
}
// 或者
for i:=0; i<3; i++ {
go func(n int){
fmt.Println(n)
}(i)
}
总结:
在for循环里如果使用了闭包并且引用了循环变量i会有data race的问题, 而且因为闭包的原因, i在循环结束后不会被释放,会被放在堆内存中, 而且可能会打印出i 最后的值(在本例子中是3)