首先要说清楚这个话题,必须要先清楚什么是垃圾回收,要清楚什么是垃圾回收呢,必须要知道什么是垃圾,所谓的垃圾就是不再需要的内存
,需要或者不需要是由人为来决定的
<!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即使是无法触达的内存空间依旧无法被回收