纯函数的核心是“守边界”:输出仅由输入决定且不读写外部状态;需不动原数据、不偷看外部、不确定值全靠参数传入,副作用应隔离封装。

在 JavaScript 函数式编程中,用纯函数避免副作用,核心不是“加功能”,而是“守边界”:只靠参数算结果,不碰外面一砖一瓦。
纯函数的两个铁律
一个函数要真正“纯”,必须同时满足:
- 输出完全由输入决定:add(2, 3) 每次都返回 5,不会因为时间、用户操作或全局变量变化而不同
- 不读不写外部状态:不改传入的对象/数组、不调用 Date.now()、Math.random()、localStorage、fetch、console.log,也不依赖闭包里可变的变量
怎么写出不破戒的纯函数
关键在“不动原数据、不偷看外面、不确定值全靠传”。常见做法包括:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 对象参数用展开符或 structuredClone 创建新副本再处理,比如
{...user, name: user.name.toUpperCase()} - 数组操作优先用 map/filter/reduce,不用 push/splice —— slice() 是纯的,splice() 不是
- 需要当前时间?让调用方把
now时间戳当参数传进来;需要随机数?把seed或具体数值传入,而不是在函数里调Math.random() - 所有依赖(配置、API 响应体、用户偏好)都显式作为参数,不从模块顶层或闭包里偷偷读取可变变量
副作用不是敌人,但得管住它
真实项目离不开 DOM 更新、网络请求、日志打印,但它们不该混在计算逻辑里。正确做法是:
- 把副作用单独封装成函数,比如
renderUser(user)、saveToApi(data),名字就说明它要干啥 - 纯函数只负责“转换”:接收原始数据,返回新数据,比如
filterActiveUsers(users)、formatPrice(price) - 在架构层面分层:Domain 层全是纯函数,Service 层调 API,UI 层触发渲染 —— 数据流清晰,哪一层出问题一眼可见
- 异步操作统一用 Promise 表达意图,不直接在纯函数里写
fetch().then(...);调试时用 tap 类工具,但确保它不改变值本身
渐进落地的小切口
不用重写整个项目,从这几个地方开始就能见效:
- 工具函数(日期格式化、金额计算、列表筛选)全部重写为纯函数
- React 组件里,把 props 转 state 的逻辑抽成纯函数,比如
deriveInitialForm(props) - Redux 或 Zustand 的 reducer 必须是纯的;API 请求这类副作用交给中间件或自定义 hook 处理
- 用
Object.freeze()或 Immer 的produce防止意外修改,尤其在调试阶段快速暴露问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










