JavaScript 闭包 内存泄漏与解决办法

闭包的优缺点:

优点:①封装功能 ②防止全局变量污染 ③ 延长变量的生命周期,缓存上一次执行的结果 ④实现局部变量/函数私有化

缺点:①浪费内存(尤其是引用了较大的对象)

注意事项:①因为调用函数会创建闭包,所以要避免嵌套调用/递归调用闭包函数


闭包垃圾回收:

由于闭包的应用场景很多,以及本人水平有限,所以以下内容可能不是很适合您所需要的场景,请您见谅~

回归正题:如下代码,返回的函数中包含了对内部数组的引用

注:引用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)

运行时堆内存图:
JavaScript 闭包 内存泄漏与解决办法_第1张图片
可以看到,在很长时间内,该数组仍然被存放在堆内存中无法释放

修改办法:加了一句代码

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) 

运行时堆内存图:
JavaScript 闭包 内存泄漏与解决办法_第2张图片
这回可以看到,堆内存很快就被释放了


原理:

JavaScript 闭包 内存泄漏与解决办法_第3张图片
如图,返回的函数中包含了数组的引用(即arr),即我们在调用fn1()时生成的getArr0对象(函数)中包含了arr变量,即数组的引用。

再次说明:new Array()用于创建数组,存放在堆中,变量arr是对这个数组的引用,指向数组,放在栈中

因为getArr0对象是定义在全局作用域上的,只能随着程序的销毁而销毁,所以getArr0对象与数组之间的引用在程序销毁前也会一直保留

如果此时手动将getArr0对象置为null,那么此时getArr0对象就立刻失去了对堆中数组的引用,此时堆中的这个数组,已经没有引用指向它了,所以会在垃圾收集器在下次垃圾清理的时候,会将这块内存回收。


总结

①解除返回的函数对象内部局部对象之间的引用是关键
②对于dom操作,如果在内部函数中添加了事件监听,那么在将内部函数调用完毕后置为null之前,先将该事件解绑

你可能感兴趣的:(02,WEB前端,javascript,前端,开发语言)