用Set.prototype.forEach配合闭包可实现无副作用批处理:不修改原Set、不依赖外部可变状态、每次基于快照处理,逻辑纯正可复用。

用 Set.prototype.forEach 配合闭包实现无副作用批处理,关键在于:不修改原 Set、不依赖外部可变状态、每次处理都基于当前快照,且逻辑可复用可预测。
明确“无副作用”的边界
所谓“无副作用”,指不改变原始 Set 的结构(不调用 add/delete/clear),也不污染全局变量或传入对象。闭包只用于封装处理逻辑与局部状态,而非替代数据源。
- ✅ 允许:在闭包内缓存中间计算结果、维护临时计数器、收集日志、触发纯通知回调
- ❌ 禁止:在
forEach回调中调用set.add()或set.delete()(除非你明确需要动态遍历) - ⚠️ 注意:
forEach遍历顺序是插入顺序,但 Set 本身无索引——回调的第二个参数(key)恒等于第一个参数(value)
构建纯处理型闭包函数
把批处理逻辑封装成一个工厂函数,返回一个只读处理器。它接收 Set 实例,内部用闭包保存配置项和聚合结果,对外暴露 run 方法:
处理 ${val} 失败:
配合追踪器实现带变更感知的只读处理
若需在处理过程中响应属性变化(如监控某个配置项是否被外部修改),可用前面提到的闭包追踪器,但仅用于读取——不触发写操作:
- 创建一个
createTrackedProp实例管理开关状态,例如isEnabled - 在
forEach回调中读取isEnabled.get()决定是否执行某步逻辑 - 监听器仅用于调试或上报,不反向修改处理流程
这样既保持了处理过程的确定性,又具备运行时响应能力,且整个链条没有对原始 Set 或环境造成突变。
避免常见陷阱
实际使用中容易忽略三点:
- 不要在 forEach 中修改 Set 同时又依赖其 size 或遍历完整性 —— 动态增删会改变迭代行为,违背“无副作用”前提
- 闭包捕获的不是引用而是值快照 —— 若传入的是对象,修改其属性仍会影响外部;应深拷贝或只读访问
-
异步操作需显式控制时序 ——
forEach不支持await,如需串行异步处理,请改用for...of或Array.from(set).map(...).Promise.all()










