纯函数是给定相同输入总返回相同输出且无副作用的函数,天然适合单元测试,因其无需模拟外部状态、重置变量或担心执行顺序。

纯函数在 JavaScript 中不是语法特性,而是一种函数编写约定:给定相同输入,总是返回相同输出,且不产生任何可观察的副作用。它天然适合单元测试——因为无需模拟外部状态、不用重置全局变量、也不用担心执行顺序。
纯函数的两个硬性条件
一个函数要被称为“纯”,必须同时满足:
- 无副作用:
console.log、document.querySelector、fetch、修改入参对象(如arr.push)、修改闭包变量等都不允许 - 输出仅依赖输入:
Math.random()、Date.now()、读取window.innerWidth等外部值会导致非纯
例如:
const add = (a, b) => a + b; // ✅ 纯
const now = () => Date.now(); // ❌ 非纯(每次调用结果不同)
const mutate = (arr) => { arr.push(1); return arr; }; // ❌ 非纯(修改了输入)
为什么纯函数让测试更简单?
测试时你只关心「输入 → 输出」映射,不需要:
- 启动 mock 服务或拦截
fetch - 保存/恢复
localStorage或 DOM 状态 - 处理异步等待或定时器(纯函数必同步)
- 担心多次
test()调用相互污染
比如测试一个格式化金额的纯函数:
const formatMoney = (cents) => `$${(cents / 100).toFixed(2)}`;
// 测试只需断言:
expect(formatMoney(1250)).toBe('$12.50');
expect(formatMoney(0)).toBe('$0.00');
常见“伪纯”陷阱:看似纯,实则不纯
这些写法容易被误认为纯,但实际违反约束:
立即学习“Java免费学习笔记(深入)”;
- 使用
new Date()或Math.random()—— 即使藏在深层调用里 - 读取或修改闭包中的变量(如缓存 map、计数器)
- 对对象参数做浅拷贝后修改属性(
{...obj}后改copy.name = 'x'仍可能影响引用) - 调用另一个非纯函数(哪怕只有一行),整条链就不再是纯的
修复方式通常是把“外部依赖”显式传入,例如:
// ❌ 隐式依赖当前时间 const isExpired = (timestamp) => Date.now() > timestamp + 86400000; // ✅ 改为接收 time 参数,测试时可自由控制 const isExpired = (timestamp, time = Date.now()) => time > timestamp + 86400000;
纯函数的价值不在“多酷”,而在降低测试的意外成本——一旦函数内部开始读写外部状态,你就要为每一次测试准备上下文、清理副作用、处理竞态,而这些开销会随代码增长指数上升。










