Pure Functions
Same inputs → same output, no side effects — why purity helps tests and UI, and how to isolate effects at the edges.
- javascript
- pure-functions
A pure function (1) returns a value determined only by its arguments, and (2) does not mutate external state or perform I/O. Same inputs → same output. Always.
// pure
function add(a, b) {
return a + b;
}
function fullName({ first, last }) {
return `${first} ${last}`;
}
// impure
let tax = 0.2;
function priceWithTax(n) {
return n * (1 + tax); // depends on external mutable tax
}
function save(user) {
localStorage.setItem('user', JSON.stringify(user)); // I/O
return user;
}
Why care in frontend work
| Benefit | How purity helps |
|---|---|
| Tests | No mocks for clock/network if core logic is pure |
| Reasoning | Refactors don’t summon hidden state bugs |
| Memoization | Safe to cache (see memoization) |
| Concurrent UI | Fewer “run twice” surprises when effects are explicit |
React’s ideal: render function pure w.r.t. props/state; effects live in useEffect / event handlers.
Mutations of arguments
// impure — mutates input
function addItem(cart, item) {
cart.items.push(item);
return cart;
}
// pure — new structure
function addItemPure(cart, item) {
return {
...cart,
items: [...cart.items, item],
};
}
Returning a new object isn’t required by math, but in UI apps immutable updates make purity and change detection easier.
Hidden impurity
function id() {
return Math.random(); // non-deterministic
}
function nowLabel() {
return Date.now(); // time
}
function readLen() {
return document.querySelectorAll('li').length; // DOM
}
Push nondeterminism to the edge:
function label(ts) {
return new Date(ts).toISOString();
}
// caller: label(Date.now())
Partial purity is still useful
async function loadUser(id, fetchImpl = fetch) {
const res = await fetchImpl(`/api/users/${id}`);
if (!res.ok) throw new Error('load failed');
return normalizeUser(await res.json()); // normalizeUser pure
}
Inject fetch for tests; keep normalizeUser pure and unit-tested hard.
Red flags in code review
- Function name is a noun computation but it writes to a module-level cache without documentation
- “Helper” that toggles DOM classes
- Selectors that both filter and kick off analytics
Interview answer (out loud)
“Pure functions depend only on inputs and have no side effects, so they’re easy to test and memoize. In UI code I keep rendering and transforms pure and push I/O, time, and random to the edges. Mutating arguments counts as impurity in practice for frontend state.”
Referential transparency
If f(x) is pure, any occurrence can be replaced by its result without changing behavior. That’s why pure helpers compose and why property-based tests work well on them.
Local mutation is OK
function sum(nums) {
let s = 0; // local mutation
for (const n of nums) s += n;
return s;
}
Purity is about observable external effects and deterministic output — not “never use let.” Mutating a locally created object and returning it is still pure if nothing outside could see the intermediate states.
Time and pure UI snapshots
function viewModel(state, now) {
return { ...state, stale: now - state.updatedAt > 60_000 };
}
Pass now in; don’t call Date.now() inside if you want snapshot tests to be stable.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.