A pure function is a function that:
// Pure function
function add(a, b) {
return a + b;
}
// Always returns the same result for the same inputs
add(2, 3); // Always returns 5
add(2, 3); // Always returns 5 (same input = same output)
Side effects are changes that a function makes outside of its own scope, such as:
let total = 0;
// Function with side effects
function addToTotal(amount) {
total += amount; // Modifies external variable
console.log(`Total is now: ${total}`); // Side effect: console output
return total;
}
addToTotal(5); // Returns 5, but also changes `total` and logs to console
addToTotal(5); // Returns 10 (different result even with same input!)
| Pure Function | Function with Side Effects |
|---|---|
| ✅ Same input → Same output | ❌ Same input → May produce different output |
| ✅ No external dependencies | ❌ May depend on external state |
| ✅ Easy to test | ❌ Harder to test (need to mock/setup state) |
| ✅ Easy to reason about | ❌ Can be unpredictable |
| ✅ Can be reused safely | ❌ May cause unexpected behavior |
Mutable means “can be changed.” A mutable value or object can be modified after it’s created.
Immutable means “cannot be changed.” An immutable value or object cannot be modified after it’s created. Instead, you create a new value.
// Mutable: modifies the original array
const numbers = [1, 2, 3];
function addNumber(arr, num) {
arr.push(num); // Modifies the original array!
return arr;
}
addNumber(numbers, 4);
console.log(numbers); // [1, 2, 3, 4] - Original array was changed!
// Immutable: creates a new array
const numbers = [1, 2, 3];
function addNumber(arr, num) {
return [...arr, num]; // Creates a new array, doesn't modify original
}
const newNumbers = addNumber(numbers, 4);
console.log(numbers); // [1, 2, 3] - Original unchanged
console.log(newNumbers); // [1, 2, 3, 4] - New array created
Pure functions should not mutate their inputs. Instead, they should return new values:
// ❌ BAD: Mutates input (not pure)
function addItemToCart(cart, item) {
cart.items.push(item); // Modifies the input!
return cart;
}
// ✅ GOOD: Returns new value (pure)
function addItemToCart(cart, item) {
return {
...cart,
items: [...cart.items, item] // Creates new object and array
};
}
// Instead of: arr.push(item)
const newArr = [...arr, item];
// Instead of: arr.pop()
const newArr = arr.slice(0, -1);
// Instead of: arr.sort()
const newArr = [...arr].sort(); // Copy first, then sort
// Instead of: arr.reverse()
const newArr = [...arr].reverse(); // Copy first, then reverse
// Instead of: obj.property = value
const newObj = { ...obj, property: value };
// Instead of: obj.nested.property = value
const newObj = {
...obj,
nested: {
...obj.nested,
property: value
}
};
| Mutable | Immutable |
|---|---|
| ❌ Modifies original value | ✅ Creates new value |
| ❌ Can cause unexpected changes | ✅ Original always safe |
| ❌ Harder to track changes | ✅ Easy to see what changed |
| ❌ Can break pure functions | ✅ Works perfectly with pure functions |
| ⚡ Faster (no copying) | 🐌 Slightly slower (creates new values) |
// Before: Mutable function
function updateUser(user, newName) {
user.name = newName; // Mutates input
return user;
}
// After: Immutable function (pure)
function updateUser(user, newName) {
return {
...user,
name: newName // Creates new object
};
}
// Usage:
const user = { name: 'Alice', age: 30 };
const updatedUser = updateUser(user, 'Bob');
console.log(user); // { name: 'Alice', age: 30 } - unchanged
console.log(updatedUser); // { name: 'Bob', age: 30 } - new object
let userCount = 0;
function incrementUserCount() {
userCount++; // Side effect: modifies external variable
return userCount;
}
function incrementUserCount(currentCount) {
return currentCount + 1; // Pure: takes input, returns output
}
// Usage:
let userCount = 0;
userCount = incrementUserCount(userCount); // Explicitly pass and assign
Sometimes side effects are necessary and appropriate:
The key is to separate pure logic from side effects:
// Pure function (logic)
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Function with side effects (UI update)
function updateDisplay(items) {
const total = calculateTotal(items); // Use pure function
document.getElementById('total').textContent = `$${total}`; // Side effect
}