javascript
examples
examples.js⚡javascript
/**
* =====================================================
* 5.9 PURE FUNCTIONS & SIDE EFFECTS - EXAMPLES
* =====================================================
* Writing predictable, testable functions
*/
// =====================================================
// 1. PURE VS IMPURE - BASIC EXAMPLE
// =====================================================
console.log('--- Pure vs Impure ---');
// ✅ Pure function
function add(a, b) {
return a + b;
}
console.log('Pure add(2, 3):', add(2, 3)); // Always 5
console.log('Pure add(2, 3):', add(2, 3)); // Always 5
// ❌ Impure function
let counter = 0;
function incrementCounter() {
counter++;
return counter;
}
console.log('Impure increment:', incrementCounter()); // 1
console.log('Impure increment:', incrementCounter()); // 2 (different!)
// =====================================================
// 2. DETERMINISTIC FUNCTIONS
// =====================================================
console.log('\n--- Deterministic Functions ---');
// ✅ Deterministic - same input, same output
function multiply(a, b) {
return a * b;
}
console.log('multiply(4, 5):', multiply(4, 5)); // Always 20
console.log('multiply(4, 5):', multiply(4, 5)); // Always 20
// ❌ Non-deterministic
function randomValue(max) {
return Math.floor(Math.random() * max);
}
console.log('randomValue(10):', randomValue(10)); // Random
console.log('randomValue(10):', randomValue(10)); // Different!
// =====================================================
// 3. AVOIDING MUTATION - ARRAYS
// =====================================================
console.log('\n--- Avoiding Array Mutation ---');
const original = [1, 2, 3, 4, 5];
// ❌ Impure - mutates original
function addItemImpure(arr, item) {
arr.push(item); // Mutates!
return arr;
}
// ✅ Pure - returns new array
function addItemPure(arr, item) {
return [...arr, item];
}
// ❌ Impure - sort mutates
function sortImpure(arr) {
return arr.sort((a, b) => a - b);
}
// ✅ Pure - copy then sort
function sortPure(arr) {
return [...arr].sort((a, b) => a - b);
}
// ✅ Pure - remove item
function removeItem(arr, index) {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
// ✅ Pure - update item
function updateItem(arr, index, newValue) {
return arr.map((item, i) => (i === index ? newValue : item));
}
console.log('Original:', original);
console.log('Added:', addItemPure(original, 6));
console.log('Original still:', original);
console.log('Removed index 2:', removeItem(original, 2));
console.log('Updated index 0:', updateItem(original, 0, 10));
// =====================================================
// 4. AVOIDING MUTATION - OBJECTS
// =====================================================
console.log('\n--- Avoiding Object Mutation ---');
const person = { name: 'Alice', age: 25 };
// ❌ Impure - mutates original
function updateAgeImpure(obj, newAge) {
obj.age = newAge;
return obj;
}
// ✅ Pure - returns new object
function updateAgePure(obj, newAge) {
return { ...obj, age: newAge };
}
// ✅ Pure - add property
function addProperty(obj, key, value) {
return { ...obj, [key]: value };
}
// ✅ Pure - remove property
function removeProperty(obj, key) {
const { [key]: removed, ...rest } = obj;
return rest;
}
console.log('Original:', person);
console.log('Updated age:', updateAgePure(person, 30));
console.log('Added email:', addProperty(person, 'email', 'alice@example.com'));
console.log('Removed age:', removeProperty(person, 'age'));
console.log('Original still:', person);
// =====================================================
// 5. NESTED OBJECT UPDATES
// =====================================================
console.log('\n--- Nested Object Updates ---');
const user = {
name: 'Bob',
address: {
city: 'NYC',
zip: '10001',
},
settings: {
theme: 'dark',
notifications: true,
},
};
// ✅ Pure - update nested property
function updateCity(user, newCity) {
return {
...user,
address: {
...user.address,
city: newCity,
},
};
}
// ✅ Pure - update deeply nested
function updateNestedProperty(obj, path, value) {
const [first, ...rest] = path;
if (rest.length === 0) {
return { ...obj, [first]: value };
}
return {
...obj,
[first]: updateNestedProperty(obj[first] || {}, rest, value),
};
}
console.log('Updated city:', updateCity(user, 'LA'));
console.log(
'Updated theme:',
updateNestedProperty(user, ['settings', 'theme'], 'light')
);
// =====================================================
// 6. PURE STRING OPERATIONS
// =====================================================
console.log('\n--- Pure String Operations ---');
// Strings are immutable in JS - all operations are pure
function formatName(firstName, lastName) {
return `${firstName.trim()} ${lastName.trim()}`.toUpperCase();
}
function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-');
}
function truncate(str, maxLength) {
if (str.length <= maxLength) return str;
return str.slice(0, maxLength - 3) + '...';
}
console.log('formatName:', formatName(' alice ', ' smith '));
console.log('slugify:', slugify('Hello World! This is a Test'));
console.log('truncate:', truncate('This is a long string', 15));
// =====================================================
// 7. PURE MATHEMATICAL FUNCTIONS
// =====================================================
console.log('\n--- Pure Math Functions ---');
function square(x) {
return x * x;
}
function average(numbers) {
if (numbers.length === 0) return 0;
return numbers.reduce((a, b) => a + b, 0) / numbers.length;
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function percentage(value, total) {
if (total === 0) return 0;
return (value / total) * 100;
}
function roundTo(value, decimals) {
const factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
}
console.log('square(5):', square(5));
console.log('average([1,2,3,4,5]):', average([1, 2, 3, 4, 5]));
console.log('clamp(15, 0, 10):', clamp(15, 0, 10));
console.log('percentage(25, 100):', percentage(25, 100));
console.log('roundTo(3.14159, 2):', roundTo(3.14159, 2));
// =====================================================
// 8. MEMOIZATION (CACHING PURE FUNCTIONS)
// =====================================================
console.log('\n--- Memoization ---');
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log(' Cache hit for', key);
return cache.get(key);
}
console.log(' Computing for', key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// Only works with pure functions!
const memoizedFactorial = memoize(function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
});
console.log('First call:', memoizedFactorial(5));
console.log('Second call:', memoizedFactorial(5));
// =====================================================
// 9. ISOLATING SIDE EFFECTS
// =====================================================
console.log('\n--- Isolating Side Effects ---');
// Pure business logic
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
function applyDiscount(total, discountPercent) {
return total * (1 - discountPercent / 100);
}
function formatCurrency(amount) {
return `$${amount.toFixed(2)}`;
}
// Usage: Pure core, side effects at edges
const orderItems = [
{ name: 'Widget', price: 10, quantity: 2 },
{ name: 'Gadget', price: 25, quantity: 1 },
];
const total = calculateTotal(orderItems);
const discounted = applyDiscount(total, 10);
const formatted = formatCurrency(discounted);
console.log('Order total:', formatted);
// =====================================================
// 10. DEPENDENCY INJECTION FOR TESTABILITY
// =====================================================
console.log('\n--- Dependency Injection ---');
// ❌ Hard to test - uses Date directly
function getAgeImpure(birthYear) {
return new Date().getFullYear() - birthYear;
}
// ✅ Testable - current year is injected
function getAgePure(birthYear, currentYear) {
return currentYear - birthYear;
}
// Factory pattern for dependency injection
function createUserService(getCurrentYear) {
return {
getAge(birthYear) {
return getCurrentYear() - birthYear;
},
};
}
// Production
const userService = createUserService(() => new Date().getFullYear());
console.log('Age:', userService.getAge(1990));
// Testing
const testUserService = createUserService(() => 2024);
console.log('Test age:', testUserService.getAge(1990)); // Always 34
// =====================================================
// 11. IMMUTABLE DATA PATTERNS
// =====================================================
console.log('\n--- Immutable Data Patterns ---');
// Todo list operations (all pure)
const todos = [
{ id: 1, text: 'Learn JS', completed: true },
{ id: 2, text: 'Build app', completed: false },
];
function addTodo(todos, text) {
return [...todos, { id: Date.now(), text, completed: false }];
}
function toggleTodo(todos, id) {
return todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
}
function removeTodo(todos, id) {
return todos.filter((todo) => todo.id !== id);
}
function updateTodoText(todos, id, text) {
return todos.map((todo) => (todo.id === id ? { ...todo, text } : todo));
}
console.log('Original:', todos);
console.log('Toggled:', toggleTodo(todos, 2));
console.log('Still original:', todos);
// =====================================================
// 12. TRANSFORMING DATA PURELY
// =====================================================
console.log('\n--- Data Transformation ---');
const users = [
{ id: 1, name: 'Alice', age: 25, role: 'admin' },
{ id: 2, name: 'Bob', age: 30, role: 'user' },
{ id: 3, name: 'Charlie', age: 35, role: 'user' },
];
// All pure transformations
function getActiveUsers(users) {
return users.filter((u) => u.role === 'admin');
}
function getUserNames(users) {
return users.map((u) => u.name);
}
function sortByAge(users) {
return [...users].sort((a, b) => a.age - b.age);
}
function groupByRole(users) {
return users.reduce((acc, user) => {
const role = user.role;
return {
...acc,
[role]: [...(acc[role] || []), user],
};
}, {});
}
console.log('Names:', getUserNames(users));
console.log(
'Sorted:',
sortByAge(users).map((u) => u.name)
);
console.log('Grouped:', groupByRole(users));
// =====================================================
// 13. FUNCTION COMPOSITION
// =====================================================
console.log('\n--- Function Composition ---');
// Compose pure functions
const pipe =
(...fns) =>
(value) =>
fns.reduce((acc, fn) => fn(acc), value);
const compose =
(...fns) =>
(value) =>
fns.reduceRight((acc, fn) => fn(acc), value);
// Pure transformation functions
const double = (x) => x * 2;
const addTen = (x) => x + 10;
const stringify = (x) => `Result: ${x}`;
// Composed pipeline
const process = pipe(double, addTen, stringify);
console.log(process(5)); // "Result: 20"
// =====================================================
// 14. HANDLING OPTIONAL VALUES PURELY
// =====================================================
console.log('\n--- Handling Optional Values ---');
// Pure functions that handle null/undefined
function getProperty(obj, path, defaultValue = undefined) {
const value = path.split('.').reduce((current, key) => current?.[key], obj);
return value ?? defaultValue;
}
const data = {
user: {
profile: {
name: 'Alice',
},
},
};
console.log('Found:', getProperty(data, 'user.profile.name'));
console.log('Not found:', getProperty(data, 'user.settings.theme', 'light'));
// =====================================================
// 15. TESTING PURE FUNCTIONS
// =====================================================
console.log('\n--- Testing Pure Functions ---');
// Pure functions are easy to test
function simpleTest(name, fn, input, expected) {
const result = fn(...input);
const passed = JSON.stringify(result) === JSON.stringify(expected);
console.log(`${passed ? '✅' : '❌'} ${name}`);
if (!passed) {
console.log(` Expected: ${JSON.stringify(expected)}`);
console.log(` Got: ${JSON.stringify(result)}`);
}
}
simpleTest('add', add, [2, 3], 5);
simpleTest('multiply', multiply, [4, 5], 20);
simpleTest('square', square, [5], 25);
simpleTest('average', average, [[1, 2, 3, 4, 5]], 3);
// =====================================================
// SUMMARY
// =====================================================
console.log('\n--- Summary ---');
console.log(`
PURE FUNCTIONS:
• Same input → same output
• No side effects
• Predictable and testable
AVOID:
• Mutating arguments
• Global state
• I/O in pure functions
• Date.now(), Math.random()
PATTERNS:
• Return new arrays/objects
• Spread operator for copies
• Dependency injection
• Function composition
• Memoization
`);