闭包节流与享元模式分工协作:前者通过闭包控制高频事件触发频次,后者复用内在状态减少对象内存开销;二者分层配合,节流为前置减负,享元为核心精简。

闭包节流和享元模式解决的是两类不同问题:前者控制事件触发频率,后者减少对象内存开销。二者可以协同使用,但不是直接“结合”成一个技术;关键在于分层分工——用闭包节流约束高频输入行为,再用享元模式管理编辑器中大量重复的轻量对象(如字符样式、光标状态、格式标记等)。真正精简内存的核心是享元,节流只是前置减负手段。
先用闭包节流压低事件调用频次
编辑器中键盘输入、鼠标移动、滚动等事件极易高频触发,若每次触发都新建对象或执行重绘逻辑,会迅速堆积临时对象、触发频繁 GC。闭包节流的作用就是把“可能密集”的调用收敛为“可控节奏”:
- 节流函数内部维护一个定时器引用和上一次执行时间戳,通过闭包捕获并持久化这些状态
- 对
keydown或input事件做 100ms 节流,意味着 1 秒内最多响应 10 次,而非每敲一个键就处理一次 - 节流回调里不做对象创建,只收集变更摘要(例如:“位置 127 字符变粗”),延迟交给享元系统批量处理
再用享元模式复用可共享的样式与符号
编辑器里真正吃内存的不是事件本身,而是伴随事件生成的大量相似对象。比如十万字文档中,可能只有 5 种字体、3 种字号、8 种颜色组合——这些就是典型的“内在状态”,适合提取为享元:
- 定义
CharacterStyle类,只含fontFamily、fontSize、color等不可变字段,不存位置、索引等上下文信息 - 用工厂类(如
StylePool)缓存已创建的样式实例,键为"Arial-14-#333"这类字符串;相同配置首次创建,后续直接返回已有引用 - 实际字符对象(
CharNode)只持有一个style引用 + 自身charCode和offset,体积从几百字节降至几十字节
避免把节流闭包本身变成内存陷阱
节流函数若不当使用,反而会因闭包持有大对象而泄漏内存:
- 不要在节流回调闭包里引用整个编辑器实例、DOM 节点树或未清理的监听器数组
- 节流函数应只捕获必要参数(如光标坐标、选区范围),且这些值宜用简单类型(number/string)而非复杂对象
- 当编辑器销毁时,需显式清除节流定时器,并将闭包引用置为
null,防止其长期持有所属作用域中的大对象
典型协同流程示例
用户连续输入 “hello”:
- 5 次
input事件在 200ms 内触发 → 节流器合并为 1 次执行 - 节流回调解析出新增字符及当前样式 → 查
StylePool获取或创建对应CharacterStyle实例(复用已有) - 仅创建 5 个极简
CharNode,每个仅含char和styleRef,不重复存储样式数据 - 最终内存占用≈5 ×(2 字节 char + 8 字节指针),而非 5 ×(样式对象深拷贝 + 字符数据)

















