闭包是函数与其词法环境的组合,需同时满足:存在引用外部局部变量的内部函数、该函数被返回或脱离原始上下文;var/let在循环闭包中因作用域机制不同导致输出差异;闭包会阻止捕获变量被垃圾回收,易引发内存泄漏。
闭包不是某种特殊函数,而是函数与其词法环境的组合——只要一个函数在定义时能访问其外层
作用域
中的变量,并且这个函数在定义之外被调用,就形成了闭包。
闭包产生的核心条件
必须同时满足三个条件:
存在一个内部函数(
或箭头函数)
该内部函数引用了外部函数的局部变量(非全局、非参数传入)
外部函数返回了这个内部函数(或以其他方式使它脱离原始执行上下文)
缺一不可。只定义不返回,或返回但没保存/调用,都不构成实际起效的闭包。
与
在闭包中的行为差异
循环中创建多个闭包时,变量声明方式直接影响结果:
立即学习
“
Java免费学习笔记(深入)
”;
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
原因:
声明的
是函数作用域共享的,所有定时器回调共用同一个
;而
每次迭代都绑定独立的绑定记录(binding),每个闭包捕获的是各自迭代中的
值。
闭包导致的内存保留问题
闭包会阻止其词法环境中变量被垃圾回收,即使外部函数早已执行完毕:
如果闭包长期存活(如绑定到全局对象、事件监听器、定时器),它所捕获的变量也会一直驻留内存
特别危险的是捕获了大对象(如
、大型数组、DOM 节点集合)
调试时可通过 Chrome DevTools 的
面板 +
查看“
”条目下的 retained size
常见误用:
中直接写匿名闭包却不提供
清理路径,导致重复绑定+内存泄漏。
闭包本身没有“副作用”,但它的生命周期和捕获逻辑很容易被忽略——真正难的不是写出闭包,而是判断某个变量是否被意外长期持有。
functionvarletfor (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
}variiletjdocumentMemoryHeap SnapshotClosureaddEventListenerremoveEventListener