A pure function follows two rules:
- Same input, same output. Given the same arguments, it always returns the same result.
- No side effects. It does not change anything outside itself: no global variables, no arguments, no DOM, no network, no files.
// Pure
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // → 5
console.log(add(2, 3)); // → 5
Impure functions
let total = 0;
// Impure: changes something outside (a side effect)
function addToTotal(amount) {
total += amount;
}
// Impure: the result depends on something outside (the current time)
function isMorning() {
return new Date().getHours() < 12;
}
// Impure: changes its argument
function addItem(list, item) {
list.push(item);
return list;
}
const cart = ["book"];
addItem(cart, "pen");
console.log(cart); // → ["book", "pen"] (the original array was changed)
Side effects are not “bad”. Every useful program has them (updating the page, saving data, calling an API). The point is to keep them separate from your calculations.
Making it pure
// Pure version: returns a new array
function addItem(list, item) {
return [...list, item];
}
const cart = ["book"];
const newCart = addItem(cart, "pen");
console.log(cart); // → ["book"]
console.log(newCart); // → ["book", "pen"]
Why pure functions are nice
| Benefit | Why |
|---|---|
| Easy to test | give input, check output, no set-up |
| Easy to understand | everything you need is in the function |
| Safe to reuse and combine | nothing else changes behind your back |
| Easy to cache | same input means same output |
| Fewer bugs | no hidden dependencies between parts of the program |
// A pure function is trivial to test
function discount(price, percent) {
return price - (price * percent) / 100;
}
console.log(discount(200, 10) === 180); // → true
console.log(discount(50, 0) === 50); // → true
Pure core, impure edges
A good structure is a pure core (calculations and decisions) with a thin outer layer that does the side effects.
// pure: decide what to show
function summarise(items) {
const total = items.reduce((sum, item) => sum + item.price, 0);
return `${items.length} items, $${total}`;
}
// impure: touch the page
function render(items) {
console.log(summarise(items)); // in a page: element.textContent = summarise(items)
}
render([{ price: 4 }, { price: 6 }]); // → 2 items, $10
Spotting impurity
Ask: does this function…
- read or write a variable it did not receive?
- change an array or object it was passed?
- use
Math.random(),Date.now(), the DOM,localStorageorfetch? - log to the console or print?
If yes to any, it is impure (the last one, logging, is usually harmless).