闭包是函数与其词法环境的组合,需同时满足:存在引用外部局部变量的内部函数、该函数被返回或脱离原始上下文;var/let在循环闭包中因作用域机制不同导致输出差异;闭包会阻止捕获变量被垃圾回收,易引发内存泄漏。
闭包不是某种特殊函数,而是函数与其词法环境的组合——只要一个函数在定义时能访问其外层作用域中的变量,并且这个函数在定义之外被调用,就形成了闭包。
必须同时满足三个条件:
function 或箭头函数)
var 与 let 在闭包中的行为差异循环中创建多个闭包时,变量声明方式直接影响结果:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出 3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100); // 输出 0, 1, 2
}
原因:var 声明的 i 是函数作用域共享的,所有定时器回调共用同一个 i;而 let 每次迭代都绑定独立的绑定记录(binding),每个闭包捕获的是各自迭代中的 j 值。
闭包会阻止其词法环境中变量被垃圾回收,即使外部函数早已执行完毕:
document、大型数组、DOM 节点集合)Memory 面板 + Heap Snapshot 查看“Closure”条目下的 retained size常见误用:addEventListener 中直接写匿名闭包却不提供 removeEventListener 清理路径,导致重复绑定+内存泄漏。
闭包本身没有“副作用”,但它的生命周期和捕获逻辑很容易被忽略——真正难的不是写出闭包,而是判断某个变量是否被意外长期持有。