闭包的几种使用场景
面试速答(30 秒版 TL;DR)
- 闭包常用来做:封装私有状态、函数工厂、偏函数/柯里化、一次性执行/节流防抖、缓存与记忆化、依赖注入。
- 本质都是:把“状态”放在外层词法环境里,让返回的函数长期持有并按需读写。
1) 私有变量与封装(模块/对象私有状态)
function createStore() {
let state = {count: 0};
return {
inc() {
state.count += 1;
},
get() {
return state.count;
},
};
}
const s = createStore();
s.inc();
s.get(); // 1
优点:外部无法直接改 state,只能通过 API 访问。
2) 函数工厂(按配置生成函数)
function createLogger(prefix) {
return (msg) => console.log(`[${prefix}] ${msg}`);
}
const apiLog = createLogger("api");
apiLog("start");
3) 偏函数/柯里化(复用一部分参数)
const withAuth = (token) => (url) =>
fetch(url, {headers: {Authorization: `Bearer ${token}`}});
4) once:只允许执行一次
function once(fn) {
let called = false;
let result;
return (...args) => {
if (called) return result;
called = true;
result = fn(...args);
return result;
};
}
5) 防抖(debounce)/节流(throttle)里的定时器引用
function debounce(fn, wait) {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn(...args), wait);
};
}
timer 被返回函数闭包持有,能在多次调用间共享。
6) 记忆化缓存(memoization)
function memoize(fn) {
const cache = new Map();
return (x) => {
if (cache.has(x)) return cache.get(x);
const v = fn(x);
cache.set(x, v);
return v;
};
}
常见追问:闭包的代价与风险?
- 内存占用:闭包会延长外层变量生命周期;捕获大对象会放大占用。
- 泄漏风险:把闭包回调长期挂在事件监听、全局数组、缓存里,可能导致本该回收的对象无法回收。
- “陈旧闭包”:闭包捕获了旧值,后续逻辑却假设它会自动更新(前端框架里更常见)。解决思路是明确数据来源与更新方式,而不是盲目依赖捕获的变量。
速记要点(可背诵)
- 闭包的价值:把状态“留在词法环境里”,通过函数 API 暴露读写能力。
- 典型场景:私有状态、函数工厂、偏函数、once、防抖/节流、缓存。