闭包(Closure)
闭包是指函数能够访问并记住其词法作用域外的变量,即使外部函数已执行完毕。例如:
function outer() {
const name = "Alice";
function inner() {
console.log(name); // 访问外部作用域的变量
}
return inner;
}
const closure = outer();
closure(); // 输出 "Alice"
闭包可能导致内存泄漏的核心原因是:如果闭包长期持有对大对象或DOM元素的引用,这些资源无法被垃圾回收。
内存泄漏(Memory Leak)
内存泄漏是指程序中已不再需要的内存未能被释放,导致内存占用持续增长,最终可能引发性能问题或崩溃。
JS中常见的内存泄漏场景
- 意外的全局变量
function leak() { leakedVar = "意外全局变量"; // 未使用 let/const/var this.globalVar = "this指向全局(非严格模式)"; } - 未清理的定时器或回调
const intervalId = setInterval(() => {}, 1000); // 忘记调用 clearInterval(intervalId) - 残留的DOM引用
const elements = { button: document.getElementById("myButton"), }; // 即使从DOM中移除元素,elements.button仍持有引用 - 闭包不当使用
function createHeavyClosure() { const bigData = new Array(1000000).fill("*"); return () => bigData; // 闭包长期持有大数据引用 } const holdData = createHeavyClosure(); - 未移除的事件监听
window.addEventListener("resize", handleResize); // 组件销毁时未调用 removeEventListener
JS内存泄漏的解决方式
- 避免意外全局变量
使用严格模式("use strict"),强制声明变量。 - 手动释放资源
- 定时器:用
clearInterval/clearTimeout。 - DOM引用:使用后置为
null(element = null)。
- 定时器:用
- 及时解绑事件监听
// Vue/React组件销毁时 beforeUnmount() { window.removeEventListener("resize", handleResize); } - 谨慎使用闭包
确保闭包不长期持有不必要的大对象,必要时手动解除引用。 - 使用弱引用(WeakMap/WeakSet)
const weakMap = new WeakMap(); let key = { id: 1 }; weakMap.set(key, "data"); key = null; // WeakMap中的引用会自动释放 - 工具检测
- Chrome DevTools 的 Memory 面板(通过 Heap Snapshot 分析内存占用)。
performance.memoryAPI 监控内存变化。
总结
闭包是JS的核心特性,但需注意其引用链;内存泄漏的关键在于“无用资源的引用未被释放”。通过规范代码、及时清理和工具分析,可有效避免泄漏问题。