不用框架也能实现高效状态管理——通过闭包封装状态、变更与通知,提供 createstore 工厂函数返回 getstate、setstate(支持防抖)、subscribe(含取消订阅),并支持 flush、浅比较、requestanimationframe 聚合及 destroy 资源清理。

不用框架也能做出响应及时、内存干净、逻辑清晰的状态管理——关键不是堆功能,而是用闭包把“状态”“变更”“通知”三件事锁在同一个私有作用域里,彼此隔离、按需运行。
用闭包封装状态与更新逻辑
状态变量不暴露、不共享,只通过函数返回的接口访问:
- 定义一个工厂函数 createStore(initialState),内部声明
let state = initialState和const listeners = [] - 返回对象包含三个方法:getState() 返回结构化副本(如
JSON.parse(JSON.stringify(state))或structuredClone(state)),绝不返回原始引用 -
setState(update) 接收新值或更新函数(
prev => ({ count: prev.count + 1 })),更新后自动遍历并触发所有监听器 -
subscribe(cb) 把回调加入数组,并返回取消订阅函数(
() => { listeners.splice(listeners.indexOf(cb), 1) })
给 setState 加上防抖控制节奏
高频操作(比如搜索输入、拖拽坐标更新)不该立刻刷新 UI,而应等用户“停顿”后再批量响应:
- 在 setState 内部不直接执行通知,而是用闭包维护一个
let pendingTimer = null - 每次调用都先
clearTimeout(pendingTimer),再设新定时器,延迟触发listeners.forEach(cb => cb()) - 延迟时间可配置(如 150ms),既保证响应感,又避免渲染抖动
- 若需立即响应某次更新(如表单校验失败提示),可额外提供 flush() 方法强制清空 pending 并同步通知
绑定视图时保持轻量与可控
闭包不替你渲染,但给你最干净的桥接方式:
- 原生 DOM 场景:在 subscribe 回调里直接调用
render()或element.textContent = store.getState().value - 为防重复渲染,可在通知前做浅比较:
if (JSON.stringify(prevState) !== JSON.stringify(newState)) { /* 更新 */ } - 结合
requestAnimationFrame聚合多次变更,确保每帧最多刷新一次 - 不依赖虚拟 DOM 或 diff 算法,靠开发者自己决定“什么值得更新”和“怎么更新最快”
支持销毁与资源清理
状态管理器不该成为内存泄漏源头:
- 暴露 destroy() 方法:清除所有监听器、清空 timer、解除可能绑定的事件(如
window.addEventListener('resize', ...)) - 若内部用了
AbortController,在 destroy 中调用controller.abort()一键释放信号关联资源 - 避免闭包意外捕获长生命周期对象(如整个页面容器或全局配置),只传必要字段进工厂函数
- 组件卸载时主动调用 destroy,让状态实例真正“消失”,而非悬在内存里等待 GC











