闭包的优缺点:
优点:①封装功能 ②防止全局变量污染 ③ 延长变量的生命周期,缓存上一次执行的结果 ④实现局部变量/函数私有化
缺点:①浪费内存(尤其是引用了较大的对象)
注意事项:①因为调用函数会创建闭包,所以要避免嵌套调用/递归调用闭包函数
闭包垃圾回收:
由于闭包的应用场景很多,以及本人水平有限,所以以下内容可能不是很适合您所需要的场景,请您见谅~
回归正题:如下代码,返回的函数中包含了对内部数组的引用
注:引用arr是存放在栈中的,new Array(10000)创建的数组存放在堆中的
function fn1() {
let arr = new Array(10000000)
arr[0] = 1
return function () {
return arr[0] //此时,由于引用一直保存着,所以不会回收这个数组空间
}
}
let getArr0 = fn1()
let arr0 = getArr0()
console.log(arr0)
运行时堆内存图:
可以看到,在很长时间内,该数组仍然被存放在堆内存中无法释放
修改办法:加了一句代码
function fn1() {
let arr = new Array(10000000)
arr[0] = 1
return function () {
return arr[0]
}
}
let getArr0 = fn1()
let arr0 = getArr0()
getArr0 = null // + 将调用fn1()生成的函数对象(在JavaScript中,函数也是对象)置为null
console.log(arr0)
运行时堆内存图:
这回可以看到,堆内存很快就被释放了
原理:
如图,返回的函数中包含了数组的引用(即arr),即我们在调用fn1()
时生成的getArr0对象(函数)
中包含了arr变量,即数组的引用。
再次说明:new Array()用于创建数组,存放在堆中,变量arr是对这个数组的引用,指向数组,放在栈中
因为getArr0对象
是定义在全局作用域上的,只能随着程序的销毁而销毁,所以getArr0对象
与数组之间的引用在程序销毁前也会一直保留
如果此时手动将getArr0对象
置为null,那么此时getArr0对象就立刻失去了对堆中数组的引用,此时堆中的这个数组,已经没有引用指向它了,所以会在垃圾收集器在下次垃圾清理的时候,会将这块内存回收。
总结
①解除返回的函数对象
和内部局部对象
之间的引用是关键
②对于dom操作,如果在内部函数中添加了事件监听,那么在将内部函数调用完毕后置为null之前,先将该事件解绑