闭包是JavaScript函数作用域机制自然产生的行为:一个函数能记住并访问它定义时所在的作用域,即使在别处执行;满足内层函数定义于外层函数中、引用外层局部变量、且被带出外层作用域三条件即构成闭包。

闭包不是某种语法糖或新特性,而是 JavaScript 函数作用域机制自然产生的行为:**一个函数能记住并访问它定义时所在的作用域,即使这个函数在别处执行**。
你写了一个内部函数,又把它返回出去、传给定时器、绑给事件、或者存进变量——只要它还能读到外层函数的局部变量,那它就是闭包。不需要刻意“创建”,只要满足条件,它就存在。
怎么一眼认出闭包?看三件事
判断一段代码是否构成闭包,只盯住三点:
- 有没有一个
function定义在另一个function内部; - 这个内层函数有没有引用外层函数的局部变量(
var/let/const声明的); - 这个内层函数有没有被“带出”外层函数作用域(比如作为返回值、赋给全局变量、传给
setTimeout等)。
三者同时满足,就是闭包。例如:
function makeAdder(x) {
return function(y) {
return x + y; // ← 引用了外层的 x
};
}
const add5 = makeAdder(5); // ← 内部函数被返回并赋值
console.log(add5(3)); // 8 → 闭包生效
闭包最常用、也最容易翻车的三个场景
实际开发中,你几乎每天都在用闭包,只是没叫它名字:
-
封装私有状态:比如
createCounter()里那个count变量,外部永远碰不到,只能通过返回的函数操作——这不是设计模式,是闭包的默认能力; -
事件/定时器中保存上下文:循环绑定点击事件时,如果直接写
for (let i = 0; i console.log(i); },靠的是let块级作用域;但如果用var,就得靠闭包“捕获”当前i值,否则全输出3; -
缓存计算结果(memoization):像
memoizedAdd()把cache对象封在闭包里,每次调用都复用同一份缓存,而不是反复新建对象。
为什么有时候内存涨得快?闭包不是背锅侠
闭包本身不导致内存泄漏,但它让变量无法被垃圾回收——这是双刃剑。常见坑点:
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
立即学习“Java免费学习笔记(深入)”;
- 把整个
DOM节点、大数组、或XMLHttpRequest实例塞进闭包,又忘了清理; - 在长生命周期对象(如单页应用的根组件)里反复生成闭包,每个都持有一份冗余数据;
- 用
setTimeout或setInterval创建闭包后,忘记clearTimeout,导致闭包和它引用的数据一直活着。
检查方法很简单:打开 Chrome DevTools → Memory → Take heap snapshot,筛选“Closure”,看哪些变量意外驻留。
要不要避免闭包?别想太多,但得知道它在哪
你没法不用闭包——React的useState、Vue的响应式依赖收集、甚至addEventListener的回调,底层都依赖闭包语义。真正该做的,是写完函数后问自己一句:这个函数会活多久?它抓着哪些东西不放?这些东西还必要吗? 答案比“是不是闭包”重要得多。










