闭包有哪些表现形式?
面试速答(30 秒版 TL;DR)
- 闭包不是“某种语法”,而是函数引用了外层自由变量时自然形成的机制。
- 常见表现:返回函数、回调函数、IIFE(立即执行函数)创建私有状态、模块模式、多个函数共享同一份外层状态。
- 判断口诀:只要函数用到了“不是自己作用域里声明的变量”,就是闭包。
1) 返回函数(函数工厂)
function makeAdder(a) {
return function add(b) {
return a + b;
};
}
const add10 = makeAdder(10);
add10(5); // 15
add 用到了 a,而 a 来自外层 makeAdder,所以 add10 是闭包。
2) 回调/异步(定时器、事件、Promise)
function start() {
const label = "task-1";
setTimeout(() => {
console.log(label);
}, 100);
}
start();
setTimeout 的回调稍后执行,但仍访问 label,因此形成闭包。
3) IIFE 创建私有状态(模块模式)
const store = (() => {
let x = 0;
return {
inc() {
x += 1;
return x;
},
get() {
return x;
},
};
})();
store.inc(); // 1
store.get(); // 1
外部拿不到 x,但通过方法读写它:这是闭包实现“私有变量”的经典用法。
4) 多个函数共享同一份外层状态
function makeBox() {
let v = 0;
return {
set(x) {
v = x;
},
get() {
return v;
},
};
}
const b = makeBox();
b.set(42);
b.get(); // 42
set/get 都闭包引用同一个 v,所以能共享状态。
5) 常见面试陷阱:不是所有“函数里访问变量”都是闭包
- 访问全局变量不算“捕获外层局部变量”的典型闭包点,但从定义上讲它依然使用了自由变量。
- 访问
this不是闭包:this的绑定规则是动态的(由调用方式决定),不等同于词法环境捕获。
更稳的口径:闭包讨论重点是“捕获外层词法变量”。
易错点/坑
- 把“回调”跟“闭包”画等号:回调不一定用到外层变量;用到了才是闭包问题的核心。
- 把“闭包 = 内存泄漏”画等号:闭包只是延长生命周期,是否泄漏取决于引用是否被长期持有。
速记要点(可背诵)
- 表现形式:return 函数、回调/异步、IIFE 模块、共享状态对象。
- 判断:函数用到了外层自由变量。