跳到主要内容

闭包的几种使用场景

面试速答(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、防抖/节流、缓存。