这个问题是 JS 面试高频题 👍
而且如果你有 7 年前端经验,面试官会期待你说到:
- 原理
- 作用域链
- 内存机制
- 实际应用场景
- 优缺点
我给你一套「中高级面试回答版本」。
一、什么是闭包?
一句话定义:
闭包是函数和其词法作用域的组合。
通俗一点:
函数可以记住并访问它定义时的作用域
即使函数在作用域外执行
二、核心本质(一定要讲)
闭包产生的本质原因:
函数执行后,其内部变量没有被销毁
因为有外部引用在使用它
本质是:
函数 + 作用域链 + 垃圾回收机制
三、最经典例子
scss
function outer() {
let count = 0;
return function inner() {
count++;
console.log(count);
}
}
const fn = outer();
fn(); // 1
fn(); // 2
为什么 count 没被销毁?
因为:
sql
inner 函数引用了 outer 的变量
outer 的执行上下文不能被回收
这就是闭包。
四、从作用域链角度解释(进阶说法)
JS 在创建函数时,会保存:
lua
[[Environment]]
也就是函数定义时所在的词法环境。
即使 outer 执行结束:
sql
inner 依然持有 outer 的作用域引用
五、闭包的应用场景(面试重点 ⭐⭐⭐)
1️⃣ 数据私有化(最经典)
javascript
function createCounter() {
let count = 0;
return {
add() { count++ },
get() { return count }
}
}
const counter = createCounter();
实现:
私有变量
这在早期 JS 中是实现"类私有属性"的方式。
2️⃣ 防抖 / 节流
javascript
function debounce(fn, delay) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(fn, delay);
}
}
timer 被闭包保存。
3️⃣ 循环绑定事件(经典面试题)
css
for (var i = 0; i < 3; i++) {
(function(i){
setTimeout(() => {
console.log(i);
}, 1000)
})(i)
}
利用闭包保存每次的 i。
(当然现在可以用 let)
4️⃣ 模块化(早期 IIFE)
javascript
const module = (function(){
let privateVar = 1;
return {
get() {
return privateVar;
}
}
})();
5️⃣ React Hooks 本质
比如:
scss
useState()
内部就是通过闭包保存状态。
六、闭包的优缺点
优点
- 数据私有
- 延长变量生命周期
- 实现函数式编程
缺点
- 容易造成内存泄漏
- 滥用会增加内存占用
七、什么时候会导致内存泄漏?
如果闭包引用了:
DOM 节点
大对象
长期不释放
就会导致 GC 无法回收。
比如:
javascript
function fn() {
const dom = document.getElementById('box');
return function() {
console.log(dom);
}
}
只要返回函数没释放,dom 就不会被回收。
八、面试标准回答模板(你可以直接背)
闭包是函数和其词法作用域的组合。
当内部函数引用外部函数变量时,外部函数执行完后变量不会被销毁,从而形成闭包。
本质是作用域链和垃圾回收机制的结果。
常见应用包括数据私有化、防抖节流、模块化封装、事件绑定等。