纯函数必须显式接收全部依赖参数、不修改输入数据、无任何副作用,确保相同输入恒得相同输出,便于测试、缓存与并发安全。

输入必须完全显式,不隐含任何外部依赖
纯函数不能读取或依赖任何函数体外的变量、状态或运行时环境。比如不能直接用 Date.now() 获取当前时间,也不能访问全局对象 window 或模块顶层声明的 let user = {...}。所有需要的数据都得作为参数传进来——时间戳、随机数种子、配置项、用户数据本身,一个都不能少。
常见错误写法:
-
const getName = () => currentUser.name;❌ 依赖外部可变对象 -
const getRandomId = () => Math.random().toString(36).substr(2, 9);❌ 输出不可预测
正确做法是把依赖“拉进来”:
const getName = (user) => user?.name ?? '';-
const getRandomId = (seed) => `${seed}-${Math.abs(hash(seed)).toString(36).slice(0, 8)}`;(用确定性哈希替代 Math.random)
绝不修改原始输入,坚持不可变操作
JavaScript 中对象和数组是引用类型,直接修改参数会污染调用方的数据。纯函数必须创建新副本再处理,而不是在原对象上 .push()、.sort() 或 obj.x = 1。
- 数组:用 map/filter/reduce/concat,不用 forEach/pop/unshift
- 对象:用展开语法 {...obj, key: newVal} 或 Object.assign({}, obj, {key: newVal}),避免 obj.key = newVal
- 深层嵌套对象可用 structuredClone(现代环境)或递归浅拷贝工具,但注意性能开销
副作用必须彻底隔离,只描述不执行
网络请求、DOM 更新、console.log、localStorage、定时器等,统统不能出现在纯函数内部。它们不是“计算”,而是“动作”。要让这些副作用变得可控,就把它包装成可延迟执行的值,比如:
- 用 IO 类型封装:
const log = msg => ({ type: 'IO', run: () => console.log(msg) }); - 用函数返回 Promise 而非立即执行:
const fetchUser = id => () => fetch(`/api/users/${id}`); - 组件中把副作用推到顶层:
useEffect(() => { loadUser(); }, []);,而parseUser(data)保持 100% 纯净
测试与验证:用“相同输入 → 相同输出”反向检验
写完一个函数,立刻用几组固定输入手动跑一遍,看输出是否稳定可重现。特别注意边界情况:
- 传入空数组、null、undefined 是否仍返回确定结果?
- 两次调用
sum([1,2,3])是否都返回 6? - 如果函数里用了 new Date() 或 performance.now(),它就不是纯的
真正纯净的函数,可以放心缓存(memoize)、并行调用、随意重放,不会引发意外连锁反应。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











