纯函数是给定相同输入总返回相同输出且无副作用的函数,天然适合单元测试,因其无需模拟外部状态、重置变量或担心执行顺序。
纯函数在 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; }; // ❌ 非纯(修改了输入)
测试时你只关心「输入 → 输出」映射,不需要:
fetch
localStorage 或 DOM 状态test() 调用相互污染比如测试一个格式化金额的纯函数:
const formatMoney = (cents) => `$${(cents / 100).toFixed(2)}`;
// 测试只需断言:
expect(formatMoney(1250)).toBe('$12.50');
expect(formatMoney(0)).toBe('$0.00');
这些写法容易被误认为纯,
但实际违反约束:
new Date() 或 Math.random() —— 即使藏在深层调用里{...obj} 后改 copy.name = 'x' 仍可能影响引用)修复方式通常是把“外部依赖”显式传入,例如:
// ❌ 隐式依赖当前时间 const isExpired = (timestamp) => Date.now() > timestamp + 86400000; // ✅ 改为接收 time 参数,测试时可自由控制 const isExpired = (timestamp, time = Date.now()) => time > timestamp + 86400000;
纯函数的价值不在“多酷”,而在降低测试的意外成本——一旦函数内部开始读写外部状态,你就要为每一次测试准备上下文、清理副作用、处理竞态,而这些开销会随代码增长指数上升。