原生JavaScript内存泄漏成因、代码示例及正确清理方法咨询
原生JavaScript内存泄漏:成因、示例与修复方法
一、内存泄漏的具体成因
- 意外全局变量:未声明的变量会自动挂载到
window对象,长期占用内存无法被回收 - 闭包滥用:闭包会保留对外部作用域变量的引用,导致这些变量无法被垃圾回收器(GC)清理
- 未清理的DOM引用:移除DOM元素后,JS代码中仍保留对该元素的引用,导致DOM节点无法被回收
- 遗忘的定时器/间隔器:定时器回调函数引用外部变量,只要定时器不停止,关联变量就会一直被占用
- 未移除的事件监听器:DOM元素被移除后,对应的事件监听器未解绑,监听器和关联变量无法被GC回收
- 无限增长的缓存:用对象或
Map做缓存时,未设置过期或清理机制,缓存体积持续膨胀
二、引发内存泄漏的代码示例
1. 意外全局变量
function doSomething() { // 未用let/const声明,自动成为window的属性 leakVar = "我是意外泄漏的全局变量"; } doSomething();
2. 闭包滥用
function createClosure() { // 占用大量内存的对象 const bigData = new Array(1000000).fill('海量数据'); return function() { // 闭包保留了对bigData的引用,即使createClosure执行完毕,bigData也不会被回收 console.log(bigData.length); } } // 持有闭包函数的引用,导致bigData一直存在 const keepAlive = createClosure();
3. DOM引用未清理
const button = document.getElementById('myButton'); // JS对象保留对DOM元素的引用 const buttonRef = { element: button }; // 移除DOM元素,但buttonRef仍持有引用 document.body.removeChild(button);
4. 遗忘的定时器
const largeObject = { data: new Array(1000000).fill('测试数据') }; // 定时器回调引用largeObject,只要定时器运行,该对象就无法被回收 setInterval(() => { console.log(largeObject.data.length); }, 1000);
5. 未移除的事件监听器
const container = document.getElementById('container'); const handleClick = () => { console.log('容器被点击'); }; container.addEventListener('click', handleClick); // 移除容器,但事件监听器未解绑 document.body.removeChild(container);
三、内存释放与修复方法
1. 修复意外全局变量
- 开启严格模式避免未声明变量,或手动清理不再需要的变量:
'use strict'; // 严格模式下未声明变量会直接报错 function doSomething() { let leakVar = "局部变量"; // 不需要时手动释放引用 leakVar = null; } doSomething();
2. 修复闭包滥用
- 当闭包不再需要时,解除对闭包函数的引用:
function createClosure() { const bigData = new Array(1000000).fill('海量数据'); return function() { console.log(bigData.length); } } let keepAlive = createClosure(); // 不再使用闭包时,解除引用让GC回收关联变量 keepAlive = null;
3. 修复DOM引用未清理
- 移除DOM元素后,手动清除JS中对应的引用:
const button = document.getElementById('myButton'); const buttonRef = { element: button }; document.body.removeChild(button); // 清除对DOM元素的引用 buttonRef.element = null;
4. 修复遗忘的定时器
- 使用
clearInterval/clearTimeout清除不再需要的定时器,同时释放关联变量:
const largeObject = { data: new Array(1000000).fill('测试数据') }; const timer = setInterval(() => { console.log(largeObject.data.length); }, 1000); // 停止定时器 clearInterval(timer); // 释放大对象的引用 largeObject.data = null;
5. 修复未移除的事件监听器
- 移除DOM元素前,先解绑对应的事件监听器:
const container = document.getElementById('container'); const handleClick = () => { console.log('容器被点击'); }; container.addEventListener('click', handleClick); // 移除前先解绑监听器 container.removeEventListener('click', handleClick); document.body.removeChild(container);
6. 缓存的合理管理
- 给缓存设置过期时间,或限制缓存大小,定期清理无用数据:
const cache = new Map(); // 添加缓存时记录时间戳 function addToCache(key, value) { cache.set(key, { value, timestamp: Date.now() }); } // 清理1小时前的过期缓存 function cleanCache() { const now = Date.now(); for (const [key, item] of cache.entries()) { if (now - item.timestamp > 3600000) { cache.delete(key); } } } // 每小时执行一次缓存清理 setInterval(cleanCache, 3600000);
内容的提问来源于stack exchange,提问作者King是太阳
相关产品推荐
相关产品推荐

