多前端同学可能觉得这是浏览器或引擎该操心的事, 但理解内存管理能帮你写出更高效的代码, 还能避免各种内存泄漏的坑.
一、常见的内存泄露场景
1) 意外的全局变量:
2) 被遗忘的定时器和回调:function leaky() {leak = '这是一个全局变量'; // 本意是 let leak = ...this.anotherLeak = '这也是全局的'; // 在非严格模式下,this 指向 window}// 解决方法:使用严格模式 "use strict"
// 定时器泄漏let someData = getHugeData();setInterval(() => {let node = document.getElementById('node');if(node) {node.innerHTML = JSON.stringify(someData);}}, 1000);// 即使移除 DOM 节点,定时器还在引用 someData 和 node
3) 闭包的不当使用:// 事件监听泄漏function setup() {let btn = document.getElementById('myButton');btn.addEventListener('click', onClick);}function onClick() {console.log('clicked!');}// 如果 btn 被移除但没解绑事件,相关内存无法释放
4) DOM引用未清理:function outer() {let bigArray = new Array(1000000).fill('*');return function inner() {console.log('inner');// 即使不用 bigArray,它也会被保留,因为闭包保留了整个作用域链};}let hold = outer(); // bigArray 无法被回收
let elements = {button: document.getElementById('myButton'),div: document.getElementById('myDiv')};function removeDiv() {document.body.removeChild(elements.div);// 虽然从 DOM 移除了,但 elements.div 仍引用着这个节点}
二、内存优化技巧
1) 合理使用变量作用域:
2) 及时清理引用:// 不好的做法function process(data) {let result = [];// 大数组处理...for(let i = 0; i < data.length; i++) {result.push(data[i] * 2);}// 后续只使用 result 的前10项return result.slice(0, 10);// 但整个 result 数组会保留在内存中}// 更好的做法function processBetter(data) {let result = [];for(let i = 0; i < 10; i++) { // 只处理需要的部分if(i < data.length) {result.push(data[i] * 2);}}return result; // 内存占用更小}
3) 分块处理大数据:// 定时器let timer = setInterval(() => {...}, 1000);// 不需要时clearInterval(timer);// 事件监听function cleanUp() {btn.removeEventListener('click', onClick);}// WeakMap/WeakSet 允许对象被垃圾回收let weakMap = new WeakMap();let obj = {};weakMap.set(obj, 'some value'); // 如果 obj 被回收,weakMap 中的条目自动消失
4) 使用对象池: 频繁创建销毁对象? 试试对象池:// 处理大数组时不要一次性处理function processLargeArray(array, chunkSize, callback) {let index = 0;function next() {let chunk = array.slice(index, index + chunkSize);index += chunkSize;if(chunk.length) {callback(chunk, next);}}next();}// 使用processLargeArray(hugeArray, 1000, (chunk, next) => {// 处理当前块processChunk(chunk);// 使用 setTimeout 或 requestIdleCallback 给浏览器喘息机会setTimeout(next, 0);});
class ObjectPool {constructor(createFn) {this.createFn = createFn;this.pool = [];}get() {return this.pool.length ? this.pool.pop() : this.createFn();}release(obj) {// 重置对象状态(如果需要)this.pool.push(obj);}}// 使用let particlePool = new ObjectPool(() => ({x: 0, y: 0, vx: 0, vy: 0}));// 需要时let particle = particlePool.get();// 使用完后particlePool.release(particle);
![]() |
Austin Liu 刘恒辉
Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |




浙公网安备 33010602011771号