第 9 卷
原因:未使用`var/let/const`声明的变量,会被默认挂载到浏览器的`window`对象或Node.js的 `global`对象上。全局对象的引用会一直存在直到程序终止,导致这些变量对应的内存无法被回收。
示例:
Code block
1function handleData() {
| 2 | //遗漏let/const,data成为全局变量(window.data) |
|---|---|
| 3 | data = { list: new Array(100000) }; |
| 4 | //函数执行完后,data仍被window引用,无法回收 |
| 5 | } |
| 6 | handleData(); |
- 闭包引用未及时释放
原因:闭包会永久保留外部函数的作用域,若闭包被长期引用(如作为全局变量、事件回调),则外部函数中的变量/对象会被持续持有,即使外部函数执行完毕,这些变量也无法被GC回收。
示例:
Code block
1function createClosure() { 2// 大对象,占用大量内存
| 3 | const largeObj = new Array(1000000).fill(0); |
|---|---|
| 4 | return function() { |
| 5 | //闭包引用largeObj,导致其无法回收 |
| 6 | console.log(largeObj.length); |
| 7 | }; |
| 8 | } |
| 9 | //全局变量长期引用闭包 |
| 10 | const persistentClosure = createClosure(); |
- 未移除的事件监听
原因:为DOM元素、`window`、`EventEmitter`(Node.js)绑定的事件监听,若元素被移除、组件销毁或业务逻辑结束后,未通过`removeEventListener`移除监听,监听函数及它引用的所有对象
会被持续持有,无法回收。
示例(浏览器):
4} 5// 绑定事件
| 6 | btn.addEventListener('click', clickHandler); |
|---|---|
| 7 | //移除DOM元素,但未移除事件监听 |
| 8 | document.body.removeChild(btn); |
| 9 | // clickHandler仍被事件系统引用,无法回收 |
- 未清理的定时器/计时器
原因:`setTimeout`、`setInterval`创建的定时器,若不再需要但未通过`clearTimeout`、 `clearInterval`清理,定时器的回调函数及引用的对象会被浏览器/Node.js的事件循环持续引用,导
致内存无法释放。尤其是`setInterval`,会重复执行并持续占用内存。
示例:
Code block
| 1 | const userData = { name: '张三', age: 25 }; |
|---|---|
| 2 | //定时器回调引用userData |
| 3 | const timer = setInterval(() => { |
| 4 | console.log(userData.name); |
| 5 | }, 1000); |
| 6 | //业务结束后未清理定时器,userData和timer均无法回收 |
- 僵尸DOM(DOM元素引用丢失)
原因:DOM元素被JS变量引用,当该DOM元素从页面中移除后,JS变量未被清空,仍持有DOM元素的引用。此时DOM元素同时被JS和DOM树引用(移除后DOM树引用消失,但JS引用仍存在),导致
DOM对象无法被GC回收,成为“僵尸DOM”。
示例:
Code block
1// 引用DOM元素
| 2 | let container = document.getElementById('container'); |
|---|---|
| 3 | //移除DOM元素 |
| 4 | document.body.removeChild(container); |
| 5 | //未清空JS引用,container仍指向DOM对象,导致其无法回收 |
示例:
Code block
1function fetchData() {
| 2 | const largeData = new Array(1000000); |
|---|---|
| 3 | return new Promise((resolve) => { |
| 4 | //未调用resolve,Promise长期pending,largeData被持续引用 |
| 5 | // resolve(largeData); |
| 6 | }); |
| 7 | } |
| 8 | //调用后未处理,largeData无法回收 |
| 9 | fetchData(); |
- 无限制的内存缓存
原因:使用对象、Map、WeakMap以外的结构作为内存缓存时,若未设置缓存过期时间、未限制缓存大小,数据会持续累积,占用的内存无法被回收,最终导致内存泄漏。
示例:
Code block
1// 无清理机制的缓存对象 2const cache = {};
3// 持续向缓存添加数据,无上限 4function setCache(key, value) {
5cache[key] = value;
6} 7// 业务运行中不断调用,缓存越来越大,内存无法释放
- Node.js特有的内存泄漏
除上述通用场景外,Node.js中还有专属泄漏场景:
- 未关闭的文件句柄(`fs.open`后未调用`fs.close`);
- 未关闭的网络连接(TCP/UDP/WebSocket连接未销毁);
- `EventEmitter`重复绑定事件且未移除,导致引用累积;
- 子进程(`child_process`)未正常退出,持续占用资源。
全站分卷索引 · 10 卷
- 1第 1 卷
- 2第 2 卷
- ◦ 处理样式优先级(!important > 行内样式 > ID选择器 > 类/伪类/属性选择器 > 标签/伪元素选择
- 关键特性
- 6. 详述浏览器加载和执行JavaScript文件的机制
- JS加载执行核心机制(单线程+阻塞特性)
- • 浏览器解析HTML时,遇到 <script> 标签,立即暂停后续HTML解析,发起HTTP请求加载JS文
- • 可通过属性优化加载:
- ◦ async:异步加载,加载完成后立即执行(执行顺序不确定,不阻塞HTML解析,阻塞渲
- ◦ defer:异步加载,HTML解析完成后、DOMContentLoaded事件前执行(执行顺序与标签
- ◦ type="module":ES6模块加载,默认异步,遵循 defer 规则,支持按需加载、作用域隔
- • V8引擎采用即时编译(JIT):将AST转换为字节码,再将热点代码(频繁执行)编译为机器码,
- 关键特性
- • 异步JS(async/defer/module)仅解除对HTML解析的阻塞,仍会阻塞 DOMContentLoaded 事
- 3第 3 卷
- 4第 4 卷
- 5第 5 卷
- 6第 6 卷
- ✅ 2. 微任务的优先级和执行时机不同
- ✅ 3. 宏任务的类型和优先级不同
- ✅ 4. 页面渲染的差异
- • Node.js事件循环:无页面渲染环节,Node.js是服务端运行环境,无需渲染页面,专注于I/O处
- ✅ 5. 异步API的支持差异
- • 浏览器:支持DOM事件、AJAX、requestAnimationFrame、MutationObserver等前端专属异步
- • Node.js:支持文件I/O、网络I/O、child_process、setImmediate、process.nextTick等服务端专
- ✅ 6. 任务队列的数量差异
- ✅ 核心差异总结表
- 17. 描述process.nextTick在Nodejs事件循环中的执行顺序及其与微任务
- 的关系
- process.nextTick的定义
- 7第 7 卷
- 8第 8 卷
- 9第 9 卷
- 10第 10 卷
- • 不再使用的全局变量,手动置空释放:`window.data = null`(浏览器)、`delete global.data`
- • 避免闭包长期引用大对象,若必须引用,业务结束后手动清空闭包引用:`persistentClosure =
- • DOM元素移除前,通过`removeEventListener`移除对应的事件监听(注意:监听函数必须与绑
- • 组件生命周期销毁时(如React的`componentWillUnmount`、Vue的`beforeUnmount`),批
- • 定时器/计时器使用后,务必通过`clearTimeout`/`clearInterval`清理,尤其是在组件销毁、页
- • 将定时器ID存储在实例属性中,便于后续清理:`this.timer = setTimeout(...)`,销毁时执行
- • 避免在JS中长期缓存DOM元素,优先通过`document.querySelector`动态获取,减少不必要的
- • 异步任务(Promise/AJAX)确保完成:无论成功/失败,都调用`resolve/reject`;使用
- • 内存缓存设置上限和过期机制:使用`lru-cache`等成熟缓存库(自动淘汰最近最少使用的缓
- • 优先使用`WeakMap/WeakSet`作为缓存:它们的键是弱引用,当键对应的对象无其他引用时,会
- • 浏览器端:使用Chrome开发者工具(Memory面板),通过HeapSnapshot(堆快照)对比内存
- • Node.js端:使用`--inspect`参数启动服务,通过Chrome开发者工具调试;使用`clinic.js`、