首先要说清楚这个话题,必须要先清楚什么是垃圾回收,要清楚什么是垃圾回收呢,必须要知道什么是垃圾,所谓的垃圾就是不再需要的内存
,需要或者不需要是由人为来决定的
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><button>点击</button></body><script>const createIncrease = () => {const doms = new Array(10000).fill(0).map((_, i) => {const dom = document.createElement("div");dom.innerHTML = 1;return dom;});const increase = () => {doms.forEach((e) => {e.innerHTML = Number(e.innerHTML) + 1;});};return increase;};const increase = createIncrease();const btn = document.querySelector("button");btn.addEventListener("click",increase);</script>
</html>
就比如上面的代码,这个doms很明显是需要的内存,因为每次点击按钮都会执行increase这个函数,这个函数里面用到了doms,所以doms不是垃圾
const nums = [1,2,3,4,5];const sum = nums.reduce((pre,next) =>{return pre + next},0);
console.log(sum);
上面的nums是不是垃圾呢,不是垃圾,虽然看起来这三行代码运行结束后,没有再需要nums的地方了,按道理是不再需要的东西了,但是可以在浏览器的控制台打印这个nums,所以它不是垃圾
所以需不需要得问自己,毕竟是我们在写代码,只有我们清楚后续代码还需不需要,所以垃圾就是我们能清楚知道后续不再使用的内存
现在搞清楚了垃圾,那么再来搞清楚垃圾回收,js里面是有一个垃圾回收器来帮助我们回收不再需要的内存,但是这个玩意儿它根本不知道这个内存需不需要,就比如上面的nums不也没有回收嘛,但是它知道有一些东西一定是我们不需要的,那就是连我们自己都访问不到的内存,举个例子
let nums = [1,2,3];nums = [4,5];const sum = nums.reduce((pre,next) =>{return pre + next},0);console.log(sum);
nums被重新赋值后,很明显,数组[1,2,3]的内存我们已经没有任何机会再访问到了,被称为无法触达的内存空间,这一类内存就会被垃圾回收器定义为垃圾,这就是垃圾回收
那什么是内存泄漏呢,就是我们不再需要使用的内存空间依旧能够触达,导致垃圾回收器并不能将其回收,也就是上面例子中的nums,当内存泄漏过多的时候,就会影响代码的运行,因此需要手动将其变成无法触达的内存空间,操作很简单,在代码最后将nums赋值为null,那么数组[1,2,3]的内存空间将变成无法触达,也就会被垃圾回收器回收了
let nums = [1,2,3];const sum = nums.reduce((pre,next) =>{return pre + next},0);nums = null;console.log(sum);
所以不仅仅是闭包才会造成内存泄漏,正常定义了变量最后没有设置为null也会导致内存泄漏
闭包内存泄漏的原因主要有两点:
- 持有了不再需要的函数引用,会导致函数关联的词法环境无法销毁,从而导致内存泄漏
- 当多个函数共享词法环境时,会导致词法环境膨胀,从而导致出现无法触达但也无法回收的内存空间,从而导致内存泄漏
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><button>点击</button></body><script>const createIncrease = () => {const doms = new Array(10000).fill(0).map((_, i) => {const dom = document.createElement("div");dom.innerHTML = 1;return dom;});const increase = () => {};const test = () =>{doms}return increase;};const increase = createIncrease();const btn = document.querySelector("button");btn.addEventListener("click",() =>{increase()});</script>
</html>
上面的例子中,increase函数并没有使用doms,test函数中使用了,但是test函数根本访问不到,但是因为它和increase函数共享词法环境,导致doms即使是无法触达的内存空间依旧无法被回收