前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库JavaScript 闭包
JSJavaScript作用域与函数

JavaScript 闭包是什么?

闭包让函数记住定义时的词法环境。即使外层调用已经结束,它仍能访问、修改那份变量。

前端进阶之旅 · 一题精讲更新于 2026.09.05
JavaScript#作用域与函数
先看核心答案读代码示例
理解线索

函数记住的是哪一份环境?

  1. createCounter()创建独立的 count 绑定
  2. 返回的函数保留对这份绑定的访问
  3. counter()下一次调用继续修改它

外层调用结束 ≠ 相关变量立即消失

核心回答

先记住这个答案

闭包是函数与其定义时词法环境的组合。函数能够沿着定义时的作用域链访问外层变量,即使外层函数已经执行结束。它保留的是对变量绑定的访问能力,所以后续调用可以读到更新后的值,而不是创建时拍下的一张快照。

  • 作用域看定义位置
  • 保留变量绑定,不是值的副本
  • 可达的引用决定内存生命周期

闭包的原理:函数和词法作用域

判断一个函数能访问哪些变量,要看它在源代码中定义在哪里。函数在别处执行,并不会改用调用位置的作用域。外层函数返回,只表示这次调用结束;仍被返回函数使用的词法绑定可以继续存在。

下面的 counter 和 anotherCounter 来自两次工厂调用,所以各有一份 count。counter 连续输出 1、2,说明它读写的是同一绑定;anotherCounter 输出 1,则说明不同调用之间没有共享这份状态。

两个计数器,会共享 count 吗?JavaScript
function createCounter() {
  let count = 0;
  return function increment() {
    count += 1;
    return count;
  };
}

const counter = createCounter();
const anotherCounter = createCounter();
console.log(counter());
console.log(counter());
console.log(anotherCounter());
查看输出与解释
1
2
1

第一次工厂调用对应的 count 变为 2,第二次调用创建的 count 只增加到 1。函数体相同,词法环境各自独立。

闭包的作用:把状态和行为放在一起

JS 闭包常用来保存函数需要跨调用复用的状态。关键不是“套了一层函数”,而是这份状态是否有清晰的归属和清理时机。

验证“JavaScript 闭包”中的“闭包的作用:把状态和行为放在一起”时,应固定输入、运行环境与触发顺序,并同时记录正常结果和失败结果。这样能确认本节结论来自目标机制,而不是缓存、旧状态或偶然时序。

封装私有状态
计数器、权限配置等只通过返回的方法访问,避免把可变状态挂到全局。多个实例需要隔离时,每次调用工厂创建一份环境。
保存定时器
防抖返回的函数共享一个 timer,每次输入清掉上次任务后重新安排。这是把闭包用于真实交互的典型例子。
缓存计算结果
记忆化函数用闭包保存 Map。缓存键的设计、容量上限与失效策略同样重要,不能只写“命中就返回”。

循环里的 var 和 let,为什么输出不同?

先不引入定时器,只把回调存进数组。这样能把“闭包到底读哪一个变量”和“回调什么时候执行”两个问题分开。var 版本里的回调共享一个 i;for 循环的 let 声明会为各次迭代建立独立绑定。

验证“JavaScript 闭包”中的“循环里的 var 和 let,为什么输出不同?”时,应固定输入、运行环境与触发顺序,并同时记录正常结果和失败结果。这样能确认本节结论来自目标机制,而不是缓存、旧状态或偶然时序。

先创建回调,再统一调用JavaScript
const shared = [];
for (var i = 0; i < 3; i++) {
  shared.push(() => i);
}
console.log(shared.map(fn => fn()).join(', '));

const separate = [];
for (let j = 0; j < 3; j++) {
  separate.push(() => j);
}
console.log(separate.map(fn => fn()).join(', '));
查看输出与解释
3, 3, 3
0, 1, 2

第一个循环结束时共享的 i 已经是 3。第二个循环的三个回调各自访问对应迭代的 j,不会都读到循环结束后的值。

闭包与内存泄漏:问题在于不再需要的引用

保存状态本身就是闭包的用途,仍在使用的状态占内存不等于泄漏。需要排查的是:业务已经不需要某个对象,它却仍通过长期存活的监听器、定时器或缓存被引用。

例如组件注册了 window 事件监听,回调又引用了一批数据。组件卸载时应移除监听、取消未完成的定时任务,并按业务清理缓存。仅仅把某个局部变量赋为 null,不能保证其它引用也同时消失。定位问题时沿堆快照中的保留路径确认是谁在持有对象。

回答前,多想一步

容易答错的地方

“只有返回函数才叫闭包”
返回函数只是最容易观察的例子。传给定时器、事件监听器的回调也可能访问定义时的外层环境。排查“JavaScript 闭包”时还要核对输入、版本和执行顺序,并用反例确认修复后的边界。
“闭包保存的是当时的值”
它访问变量绑定。多个闭包可以共享同一绑定,也可以来自不同工厂调用、访问各自独立的绑定。排查“JavaScript 闭包”时还要核对输入、版本和执行顺序,并用反例确认修复后的边界。
“用了闭包就一定内存泄漏”
先看对象是否仍然可达、是否仍有业务用途。移除无用引用、控制缓存规模,比一概避免闭包更实际。
试着用自己的话回答

面试官还会怎么问?

面试时,怎样用 30 秒解释闭包是什么?

我会说:闭包让函数在外层调用结束后,仍然访问定义时的外层变量。比如创建计数器时,把 count 留在外层,返回的 increment 每次读写同一份 count。不同工厂调用产生不同环境,所以实例可以隔离;使用中要管理好监听器、定时器和缓存的生命周期。

两个闭包可以共享同一个变量吗?

可以。在同一次工厂调用里返回 increment 和 reset,两个函数都访问同一个 count,就共享了这份绑定。若希望相互隔离,要分别调用工厂创建环境,而不是复制同一个函数引用。

闭包和 this 有什么关系?

词法变量查找与 this 绑定是两个问题。普通函数的 this 通常由调用方式决定;箭头函数没有自己的 this,会沿外层环境查找。因此不能因为一个普通函数形成了闭包,就认为它也固定了外层 this。

从一道题,走向一组知识

把知识连起来

手写与交互性能

防抖和节流有什么区别?

把闭包用在真实场景:保存 timer 与调用状态。

异步与浏览器

JavaScript 事件循环如何执行?

理解异步回调在什么时候读取闭包中的变量。

放回知识体系中复习JavaScript 进阶:闭包与作用域

参考资料

  • MDN · Closures:词法作用域与闭包

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 闭包的原理:函数和词法作用域
  3. 闭包的作用:把状态和行为放在一起
  4. 循环里的 var 和 let,为什么输出不同?
  5. 闭包与内存泄漏:问题在于不再需要的引用
  6. 容易答错的地方
  7. 面试官还会怎么问
  8. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑