💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:
- Download Node.js to run JavaScript on your computer
- Use your browser's Developer Console (Press F12) to test code snippets
- Create a
.htmlfile with<script>tags and open it in your browser
Pure Functions, Immutability & Side Effects
Master the principles that drive reliable, predictable, and scalable JavaScript applications
What You'll Learn in This Lesson
- ✓Pure vs Impure functions
- ✓Side effects & controlled effects
- ✓Immutability with Arrays & Objects
- ✓Functional Composition
- ✓Memoization & Caching
- ✓Real-world Functional Architecture
Pure Functions — The Foundation of Predictable JavaScript
A pure function is a function that, for the same input, always returns the same output and causes no side effects.
- Same input → same output
- No external state modification
Example: Pure vs Impure
Pure vs Impure Functions
Compare predictable pure functions with unpredictable impure ones
// Pure function - same input always gives same output
const add = (a, b) => a + b;
console.log(add(5, 3)); // 8
console.log(add(5, 3)); // 8 (predictable!)
// Impure function - modifies external state
let count = 0;
function increment() {
count++; // modifies external state — not pure
return count;
}
console.log(increment()); // 1
console.log(increment()); // 2 (different output each time!)Why Pure Functions Matter
Pure functions reduce bugs because they are self-contained. When debugging, you examine inputs and outputs, not the entire program's state.
Why Pure Functions Improve Performance & Testing
Pure functions enable powerful optimizations that JavaScript engines can automatically apply:
- •Memoization (automatic caching)
- •Parallel processing
- •Predictable code paths
- •Zero hidden dependencies
- •Automatic optimizations in JS engines
Memoization Example
Memoization Example
Cache function results for performance optimization
const memoize = fn => {
const cache = {};
return x => cache[x] ?? (cache[x] = fn(x));
};
const slowSquare = x => {
// Simulate expensive operation
for (let i = 0; i < 1e8; i++);
return x * x;
};
const fastSquare = memoize(slowSquare);
console.log(fastSquare(5)); // slow first time
console.log(fastSquare(5)); // instant! (cached)
console.log(fastSquare(10)); // slow first time
console.log(fastSquare(10)); // instant! (cached)Immutability — Avoiding Accidental State Corruption
Immutability means state never changes directly; new state is returned instead.
Arrays: Mutable vs Immutable
Arrays: Mutable vs Immutable
Compare mutating vs non-mutating array operations
// ❌ Non-immutable code (common bug)
const arr = [1, 2, 3];
arr.push(4); // mutates original
console.log(arr); // [1, 2, 3, 4]
// ✅ Immutable version
const arr2 = [1, 2, 3];
const newArr = [...arr2, 4]; // creates new array
console.log(arr2); // [1, 2, 3] (original unchanged)
console.log(newArr); // [1, 2, 3, 4] (new array)Objects: Mutable vs Immutable
Objects: Mutable vs Immutable
Compare mutating vs non-mutating object operations
// ❌ Mutating object directly
const user = { name: "Leo", age: 20 };
user.age = 21; // mutation!
console.log(user); // { name: "Leo", age: 21 }
// ✅ Immutable update
const user2 = { name: "Leo", age: 20 };
const updated = { ...user2, age: 21 }; // new object
console.log(user2); // { name: "Leo", age: 20 } (unchanged)
console.log(updated); // { name: "Leo", age: 21 } (new)Accidental Mutation — The #1 Cause of Hidden Bugs
Even experienced developers make mistakes by accidentally mutating data:
❌ Common Mistake
Common Mutation Mistake
How accidental mutations corrupt original data
function addScore(player, inc) {
player.score += inc; // mutation!
return player;
}
const player = { name: "Ava", score: 10 };
const updated = addScore(player, 5);
console.log(player.score); // 15 (original mutated!)
console.log(updated.score); // 15 (same reference)✅ Better Version
Immutable Update Pattern
Return new objects instead of mutating originals
const addScore = (player, inc) => ({
...player,
score: player.score + inc
});
const player = { name: "Ava", score: 10 };
const updated = addScore(player, 5);
console.log(player.score); // 10 (original unchanged!)
console.log(updated.score); // 15 (new object)Side Effects — Necessary but Dangerous
A side effect is any interaction with the outside world:
- •Modifying external variables
- •Updating the DOM
- •Fetching data
- •Writing files
- •Changing global state
- •Generating random numbers
- •Date/time access
- •Logging to console
Controlled Side Effects
Controlled Side Effects
Isolate randomness and state updates for predictable code
// ❌ Impure - side effect hidden inside
let value = 10;
function update() {
value = Math.random(); // unpredictable mutation + randomness
}
update();
console.log(value); // ???
// ✅ Better - isolate randomness and state updates
const getRandom = () => Math.random();
const updateValue = (value, random) => value + random;
const newValue = updateValue(10, getRandom());
console.log(newValue); // predictable flowPure Core + Isolated Effects = Scalable Architecture
Modern design philosophy splits code into three layers:
- Pure logic layer — calculations, transformations, business rules
- Side-effect layer — API calls, storage, DOM updates
- Coordinator layer — injects values and orchestrates
Pure Core + Isolated Effects
Separate pure logic from side effects for scalable architecture
// Pure logic (no side effects)
const calculateTotal = (cart, tax) =>
cart.reduce((sum, item) => sum + item.price, 0) * (1 + tax);
// Impure (side effect)
const fetchCart = userId =>
fetch(`/cart/${userId}`).then(res => res.json());
// Coordinator (orchestrates pure + impure)
async function process(userId) {
const cart = await fetchCart(userId);
const total = calculateTotal(cart, 0.2);
console.log("Total:", total);
}
// Usage
process(123);Architecture Benefit
This isolates risk and keeps the pure core clean. Testing becomes easier because the pure logic has no dependencies on external state.
Hidden Side Effects That Developers Forget
The following look pure, but they are NOT:
Important
Side effects are unavoidable — but detecting them means you can isolate them.
Accidental Mutation with Arrays & Objects
Many built-in methods mutate data. Know which ones!
❌ Mutating Array Methods
push()pop()shift()unshift()splice()sort()reverse()
✅ Non-Mutating Methods
concat()slice()map()filter()reduce()toSorted()toReversed()
Array Mutation Mistake
Using sort() without copying mutates the original array
// ❌ Mutation mistake
const scores = [3, 1, 2];
scores.sort((a, b) => b - a); // mutates original!
console.log(scores); // [3, 2, 1] - original changed
// ✅ Correct immutable way
const scores2 = [3, 1, 2];
const sorted = [...scores2].sort((a, b) => b - a);
console.log(scores2); // [3, 1, 2] - original unchanged
console.log(sorted); // [3, 2, 1] - new sorted arrayImmutability in Nested Structures
Shallow copies don't protect nested objects:
Immutability in Nested Structures
Shallow copies don't protect nested objects
// ❌ Shallow copy mistake
const state = { user: { name: "Kai", level: 1 } };
const copy = { ...state };
copy.user.level = 2; // also mutates original ❌
console.log(state.user.level); // 2 (original changed!)
// ✅ Correct immutable deep update
const state2 = { user: { name: "Kai", level: 1 } };
const newState = {
...state2,
user: {
...state2.user,
level: 2
}
};
console.log(state2.user.level); // 1 (original unchanged)
console.log(newState.user.level); // 2 (new state)Functional Composition with Pure Functions
Pure functions chain beautifully using functional composition:
Functional Composition
Chain pure functions using pipe for clean data transformations
const double = x => x * 2;
const square = x => x * x;
const addTen = x => x + 10;
const pipe = (...fns) => x =>
fns.reduce((v, fn) => fn(v), x);
const transform = pipe(
double, // 5 -> 10
square, // 10 -> 100
addTen // 100 -> 110
);
console.log(transform(5)); // 110
// Used everywhere:
// - data processing pipelines
// - validation layers
// - request filtering
// - business logic layers
// - game loopsReal-World Architecture Pattern
Professional engineers separate pure logic from side effects at architectural boundaries:
Real-World Architecture Pattern
Separate pure logic from side effects at boundaries
// Pure logic
const applyDiscount = (price, discount) =>
price - price * discount;
// Impure boundary (side effects isolated here)
async function checkout(cart) {
const response = await fetch("/api/pay", {
method: "POST",
body: JSON.stringify(cart)
});
const data = await response.json();
return applyDiscount(data.amount, 0.1);
}
// This separation makes:
// - testing easier
// - debugging simpler
// - scaling straightforward
// - rewriting painlessKey Takeaway
Pure functions, immutability, and controlled side effects aren't just "good practices" — they form the backbone of reliable, scalable, and bug-resistant JavaScript systems. Master these concepts to gain full control over application complexity, performance, and long-term maintainability.
Where These Patterns Appear in Real Life
Frontend Frameworks
- • React hooks & state
- • Redux reducers
- • Vue composition API
- • Svelte stores
Backend Systems
- • Express middleware
- • MongoDB query builders
- • GraphQL resolvers
- • API transformers
Data Processing
- • Functional pipelines
- • Data validation
- • ETL processes
- • Stream processing
Modern Tools
- • AI prompt pipelines
- • Event handler factories
- • Web scrapers
- • Game engines
Lesson Complete!
You've mastered pure functions, immutability, and controlled side effects — the backbone of reliable, scalable JavaScript. You now know how to write code that is predictable, testable, and bug-resistant.
Up next: Memory Management & Garbage Collection — learn how JavaScript manages memory automatically and how to avoid memory leaks in production apps.
📋 Quick Reference
| Concept | Key Rule |
|---|---|
| Pure function | Same input → same output, no side effects |
| Immutability | Return new objects/arrays, never mutate |
| Side effect | Any interaction with outside world (DOM, API, logging) |
| Memoization | Cache pure function results for performance |
| Composition | pipe(...fns)(x) — chain pure functions |
Sign up for free to track which lessons you've completed and get learning reminders.