Higher-Order Functions: Complete Guide for Developers
Master JavaScript higher-order functions: callbacks, functional programming, map, filter, reduce, composition, and practical examples with detailed explanations.
Higher-Order Functions: Complete Guide for Developers
📋 Table of Contents
- What are Higher-Order Functions?
- Why Do We Need Higher-Order Functions?
- Real-World Use Cases
- Syntax
- Internal Working
- Memory Diagram
- Data Flow Diagram
- Code Examples
- Common Mistakes
- Performance Considerations
- Interview Questions
- Cheat Sheet
- Summary
1. What are Higher-Order Functions?
A Higher-Order Function (HOF) is a function that either accepts functions as arguments or returns a function as its result.
Simple Definition
┌─────────────────────────────────────────────────────────────┐
│ HIGHER-ORDER FUNCTION DEFINITION │
└─────────────────────────────────────────────────────────────┘
A Higher-Order Function is a function that:
1. Takes one or more functions as arguments
OR
2. Returns a function as its result
OR
3. Both
Key Concept:
────────────
Functions that operate on other functions
Visual Representation
┌─────────────────────────────────────────────────────────────┐
│ HOF TYPES │
└─────────────────────────────────────────────────────────────┘
Type 1: Function as Argument
─────────────────────────────
┌──────────────────────┐
│ Higher-Order Function│
│ ┌──────────────────┐ │
│ │ Callback Function│ │
│ └──────────────────┘ │
└──────────────────────┘
Type 2: Function as Return Value
─────────────────────────────────
┌──────────────────────┐
│ Higher-Order Function│
│ ↓ │
│ ┌──────────────────┐ │
│ │ Returned Function│ │
│ └──────────────────┘ │
└──────────────────────┘
Type 3: Both
────────────
┌──────────────────────┐
│ Higher-Order Function│
│ ┌──────────────────┐ │
│ │ Callback Function│ │
│ └──────────────────┘ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ Returned Function│ │
│ └──────────────────┘ │
└──────────────────────┘
First-Class Functions
┌─────────────────────────────────────────────────────────────┐
│ FUNCTIONS AS FIRST-CLASS CITIZENS │
└─────────────────────────────────────────────────────────────┘
In JavaScript, functions are first-class citizens, meaning:
✓ Assigned to variables
✓ Passed as arguments
✓ Returned from functions
✓ Stored in data structures
✓ Have properties and methods
This enables Higher-Order Functions!
Example:
────────
const greet = function() { // Assigned to variable
return "Hello";
};
const functions = [greet]; // Stored in array
function execute(fn) { // Passed as argument
return fn();
}
function create() { // Returned from function
return function() {
return "Created";
};
}
2. Why Do We Need Higher-Order Functions?
Higher-Order Functions enable powerful programming patterns and cleaner code.
Problem Without HOFs
// Without HOFs - repetitive code
const numbers = [1, 2, 3, 4, 5];
// Double all numbers
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
// Triple all numbers
const tripled = [];
for (let i = 0; i < numbers.length; i++) {
tripled.push(numbers[i] * 3);
}
// Square all numbers
const squared = [];
for (let i = 0; i < numbers.length; i++) {
squared.push(numbers[i] * numbers[i]);
}
Solution With HOFs
// With HOFs - reusable and clean
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const tripled = numbers.map(n => n * 3);
const squared = numbers.map(n => n * n);
// Or create a reusable transformer
function transform(arr, operation) {
return arr.map(operation);
}
const doubled = transform(numbers, n => n * 2);
const tripled = transform(numbers, n => n * 3);
const squared = transform(numbers, n => n * n);
Benefits
- Code Reusability: Write once, use many times
- Abstraction: Hide implementation details
- Composition: Combine simple functions into complex ones
- Declarative Code: Focus on what, not how
- Functional Programming: Enable FP paradigm
- Cleaner Code: Less boilerplate, more readable
- Testability: Easier to test pure functions
- Maintainability: Easier to understand and modify
3. Real-World Use Cases
1. Data Transformation Pipeline
// E-commerce order processing
const orders = [
{ id: 1, items: [{ price: 10 }, { price: 20 }], status: 'pending' },
{ id: 2, items: [{ price: 15 }], status: 'completed' },
{ id: 3, items: [{ price: 30 }, { price: 40 }], status: 'completed' }
];
// Process orders using HOFs
const completedOrderTotals = orders
.filter(order => order.status === 'completed')
.map(order => ({
id: order.id,
total: order.items.reduce((sum, item) => sum + item.price, 0)
}))
.filter(order => order.total > 20);
console.log(completedOrderTotals);
// [{ id: 3, total: 70 }]
2. Event Handling
// DOM event handling with HOFs
function createClickHandler(message) {
return function(event) {
console.log(message, event.target);
};
}
const buttons = document.querySelectorAll('.btn');
buttons.forEach((button, index) => {
button.addEventListener('click', createClickHandler(`Button ${index} clicked`));
});
3. API Request Handler
// Reusable API request handler
function createApiHandler(baseUrl) {
return function(endpoint) {
return function(options = {}) {
return fetch(`${baseUrl}${endpoint}`, options)
.then(response => response.json());
};
};
}
const apiHandler = createApiHandler('https://api.example.com');
const getUsers = apiHandler('/users');
const getPosts = apiHandler('/posts');
// Usage
getUsers().then(users => console.log(users));
getPosts().then(posts => console.log(posts));
4. Validation Pipeline
// Form validation using HOFs
const validators = {
required: (message) => (value) =>
value ? null : message,
minLength: (min, message) => (value) =>
value.length >= min ? null : message,
email: (message) => (value) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : message
};
function validate(value, ...validatorFns) {
for (const validator of validatorFns) {
const error = validator(value);
if (error) return error;
}
return null;
}
// Usage
const emailError = validate(
'[email protected]',
validators.required('Email is required'),
validators.email('Invalid email format')
);
console.log(emailError); // null (valid)
5. Memoization
// Performance optimization with HOF
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('Cache hit!');
return cache.get(key);
}
console.log('Computing...');
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// Expensive calculation
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const memoizedFib = memoize(fibonacci);
console.log(memoizedFib(40)); // Computing... (slow)
console.log(memoizedFib(40)); // Cache hit! (instant)
4. Syntax
Type 1: Function Accepting Function
// ═══════════════════════════════════════════════════════════
// FUNCTION AS ARGUMENT (CALLBACK)
// ═══════════════════════════════════════════════════════════
// Basic callback
function execute(callback) {
console.log("Before callback");
callback();
console.log("After callback");
}
execute(() => console.log("Inside callback"));
// Callback with parameters
function process(data, transformer) {
return transformer(data);
}
const result = process(5, x => x * 2);
console.log(result); // 10
// Multiple callbacks
function chain(value, ...operations) {
return operations.reduce((acc, operation) => operation(acc), value);
}
const result = chain(
5,
x => x * 2, // 10
x => x + 3, // 13
x => x / 2 // 6.5
);
Type 2: Function Returning Function
// ═══════════════════════════════════════════════════════════
// FUNCTION RETURNING FUNCTION
// ═══════════════════════════════════════════════════════════
// Basic return
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Currying
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(add(1)(2)(3)); // 6
// Arrow function syntax
const multiply = a => b => c => a * b * c;
console.log(multiply(2)(3)(4)); // 24
Built-in Array HOFs
// ═══════════════════════════════════════════════════════════
// BUILT-IN HIGHER-ORDER FUNCTIONS
// ═══════════════════════════════════════════════════════════
const numbers = [1, 2, 3, 4, 5];
// map() - Transform each element
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// filter() - Select elements
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]
// reduce() - Aggregate to single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// forEach() - Execute for each element
numbers.forEach(n => console.log(n));
// find() - Find first match
const found = numbers.find(n => n > 3);
// 4
// findIndex() - Find index of first match
const index = numbers.findIndex(n => n > 3);
// 3
// some() - Check if any match
const hasEven = numbers.some(n => n % 2 === 0);
// true
// every() - Check if all match
const allPositive = numbers.every(n => n > 0);
// true
// sort() - Sort with comparator
const sorted = numbers.sort((a, b) => b - a);
// [5, 4, 3, 2, 1]
5. Internal Working
Callback Execution Flow
┌─────────────────────────────────────────────────────────────┐
│ CALLBACK EXECUTION MECHANISM │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function execute(callback) {
console.log("Start");
callback();
console.log("End");
}
execute(() => console.log("Callback"));
Execution Steps:
────────────────
1. execute() called
↓
2. "Start" logged
↓
3. callback() invoked
↓
4. "Callback" logged
↓
5. callback() returns
↓
6. "End" logged
↓
7. execute() returns
Call Stack:
───────────
┌─────────────────┐
│ callback() │ ← Step 3-5
├─────────────────┤
│ execute() │ ← Step 1-7
├─────────────────┤
│ Global │
└─────────────────┘
Function Return Mechanism
┌─────────────────────────────────────────────────────────────┐
│ FUNCTION RETURN MECHANISM │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function createMultiplier(x) {
return function(y) {
return x * y;
};
}
const double = createMultiplier(2);
const result = double(5);
Execution Steps:
────────────────
1. createMultiplier(2) called
↓
2. Inner function created
↓
3. Inner function captures 'x' (closure)
↓
4. Inner function returned
↓
5. Assigned to 'double'
↓
6. double(5) called
↓
7. Accesses captured 'x' (2)
↓
8. Returns 2 * 5 = 10
Closure Formation:
──────────────────
┌──────────────────────────────┐
│ double (returned function) │
│ ├─ Code: return x * y │
│ └─ [[Scope]]: │
│ └─ x: 2 ← Captured! │
└──────────────────────────────┘
6. Memory Diagram
┌─────────────────────────────────────────────────────────────┐
│ HOF MEMORY LAYOUT │
└─────────────────────────────────────────────────────────────┘
Code:
─────
function createCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter1 = createCounter();
const counter2 = createCounter();
Memory Structure:
─────────────────
Global Memory:
┌─────────────────────────────────────┐
│ createCounter: [Function] │
│ counter1: [Object] │
│ counter2: [Object] │
└─────────────────────────────────────┘
counter1 Closure:
┌─────────────────────────────────────┐
│ Object { │
│ increment: [Function] │
│ decrement: [Function] │
│ getCount: [Function] │
│ } │
│ [[Scope]]: │
│ └─ count: 0 → 1 → 2 → ... │
└─────────────────────────────────────┘
counter2 Closure:
┌─────────────────────────────────────┐
│ Object { │
│ increment: [Function] │
│ decrement: [Function] │
│ getCount: [Function] │
│ } │
│ [[Scope]]: │
│ └─ count: 0 → 1 → 2 → ... │
└─────────────────────────────────────┘
Each counter has its own 'count' variable!
┌─────────────────────────────────────────────────────────────┐
│ ARRAY METHOD MEMORY │
└─────────────────────────────────────────────────────────────┘
Code:
─────
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);
Memory During map():
────────────────────
┌─────────────────────────────────────┐
│ numbers: [1, 2, 3] │
│ doubled: [] ← Being built │
│ │
│ map() Iteration 1: │
│ ├─ n: 1 │
│ ├─ result: 2 │
│ └─ doubled: [2] │
│ │
│ map() Iteration 2: │
│ ├─ n: 2 │
│ ├─ result: 4 │
│ └─ doubled: [2, 4] │
│ │
│ map() Iteration 3: │
│ ├─ n: 3 │
│ ├─ result: 6 │
│ └─ doubled: [2, 4, 6] │
└─────────────────────────────────────┘
7. Data Flow Diagram
┌─────────────────────────────────────────────────────────────┐
│ MAP FLOW │
└─────────────────────────────────────────────────────────────┘
Array [1, 2, 3]
↓
┌──────────────┐
│ map() │
└──────────────┘
↓
For each element:
↓
┌──────────────┐
│ Call callback│
│ with element │
└──────────────┘
↓
┌──────────────┐
│ Get result │
└──────────────┘
↓
┌──────────────┐
│ Add to new │
│ array │
└──────────────┘
↓
New Array [2, 4, 6]
┌─────────────────────────────────────────────────────────────┐
│ FILTER FLOW │
└─────────────────────────────────────────────────────────────┘
Array [1, 2, 3, 4, 5]
↓
┌──────────────┐
│ filter() │
└──────────────┘
↓
For each element:
↓
┌──────────────┐
│ Call callback│
│ with element │
└──────────────┘
↓
┌──────────────┐
│ Check result │
└──────────────┘
↓
┌────┴────┐
│ true? │
└────┬────┘
Yes │ No
↓ ↓
Include Skip
↓
New Array [2, 4]
┌─────────────────────────────────────────────────────────────┐
│ REDUCE FLOW │
└─────────────────────────────────────────────────────────────┘
Array [1, 2, 3, 4]
Initial: 0
↓
┌──────────────┐
│ reduce() │
└──────────────┘
↓
Iteration 1:
acc: 0, curr: 1
result: 1
↓
Iteration 2:
acc: 1, curr: 2
result: 3
↓
Iteration 3:
acc: 3, curr: 3
result: 6
↓
Iteration 4:
acc: 6, curr: 4
result: 10
↓
Final Result: 10
8. Code Examples
Example 1: Custom map Implementation
// Understanding map() by implementing it
function customMap(array, callback) {
const result = [];
for (let i = 0; i < array.length; i++) {
result.push(callback(array[i], i, array));
}
return result;
}
const numbers = [1, 2, 3, 4, 5];
const doubled = customMap(numbers, n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// With index
const withIndex = customMap(numbers, (n, i) => `${i}: ${n}`);
console.log(withIndex); // ["0: 1", "1: 2", "2: 3", "3: 4", "4: 5"]
Example 2: Function Composition
// Composing multiple functions
function compose(...fns) {
return function(value) {
return fns.reduceRight((acc, fn) => fn(acc), value);
};
}
// Helper functions
const add5 = x => x + 5;
const multiply3 = x => x * 3;
const subtract2 = x => x - 2;
// Compose them
const calculate = compose(
subtract2, // Applied last
multiply3, // Applied second
add5 // Applied first
);
console.log(calculate(10)); // (10 + 5) * 3 - 2 = 43
// Pipe (left to right)
function pipe(...fns) {
return function(value) {
return fns.reduce((acc, fn) => fn(acc), value);
};
}
const calculate2 = pipe(
add5, // Applied first
multiply3, // Applied second
subtract2 // Applied last
);
console.log(calculate2(10)); // (10 + 5) * 3 - 2 = 43
Example 3: Partial Application
// Partial application with HOF
function partial(fn, ...fixedArgs) {
return function(...remainingArgs) {
return fn(...fixedArgs, ...remainingArgs);
};
}
// Original function
function greet(greeting, name, punctuation) {
return `${greeting}, ${name}${punctuation}`;
}
// Create specialized versions
const sayHello = partial(greet, "Hello");
const sayHelloJohn = partial(greet, "Hello", "John");
console.log(sayHello("Alice", "!")); // "Hello, Alice!"
console.log(sayHelloJohn("!")); // "Hello, John!"
// Practical example: API calls
function apiCall(method, endpoint, data) {
return fetch(endpoint, {
method,
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' }
});
}
const postRequest = partial(apiCall, 'POST');
const getRequest = partial(apiCall, 'GET');
// Usage
postRequest('/api/users', { name: 'John' });
getRequest('/api/users', null);
Example 4: Debounce and Throttle
// Debounce - Execute after delay
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args);
}, delay);
};
}
// Usage: Search input
const search = debounce((query) => {
console.log('Searching for:', query);
// API call here
}, 300);
// Throttle - Execute at most once per interval
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn(...args);
}
};
}
// Usage: Scroll event
const handleScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
}, 100);
window.addEventListener('scroll', handleScroll);
Example 5: Complex Data Processing
// Real-world data processing pipeline
const users = [
{ id: 1, name: 'John', age: 25, active: true, orders: 5 },
{ id: 2, name: 'Jane', age: 30, active: false, orders: 3 },
{ id: 3, name: 'Bob', age: 35, active: true, orders: 8 },
{ id: 4, name: 'Alice', age: 28, active: true, orders: 12 }
];
// Complex processing with HOFs
const result = users
.filter(user => user.active) // Active users only
.filter(user => user.orders > 5) // High-value customers
.map(user => ({ // Transform data
...user,
category: user.orders > 10 ? 'VIP' : 'Regular'
}))
.sort((a, b) => b.orders - a.orders) // Sort by orders
.map(user => user.name); // Extract names
console.log(result); // ["Alice", "Bob"]
// Alternative: Using reduce for everything
const result2 = users.reduce((acc, user) => {
if (user.active && user.orders > 5) {
acc.push({
...user,
category: user.orders > 10 ? 'VIP' : 'Regular'
});
}
return acc;
}, [])
.sort((a, b) => b.orders - a.orders)
.map(user => user.name);
console.log(result2); // ["Alice", "Bob"]
9. Common Mistakes
Mistake 1: Not Returning in map/filter
// ❌ Wrong: Forgetting to return
const numbers = [1, 2, 3];
const doubled = numbers.map(n => {
n * 2; // Missing return!
});
console.log(doubled); // [undefined, undefined, undefined]
// ✅ Correct: Return the value
const doubled = numbers.map(n => {
return n * 2;
});
// Or use implicit return
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
Mistake 2: Mutating Original Array
// ❌ Wrong: Mutating original data
const users = [{ name: 'John', age: 25 }];
const updated = users.map(user => {
user.age = 26; // Mutates original!
return user;
});
// ✅ Correct: Create new objects
const updated = users.map(user => ({
...user,
age: 26
}));
Mistake 3: Overusing Chaining
// ❌ Wrong: Too many operations, hard to read
const result = data
.map(x => x * 2)
.filter(x => x > 10)
.map(x => x + 5)
.filter(x => x < 50)
.map(x => x / 2)
.filter(x => x % 2 === 0);
// ✅ Correct: Combine operations or use reduce
const result = data.reduce((acc, x) => {
const doubled = x * 2;
if (doubled > 10) {
const added = doubled + 5;
if (added < 50) {
const halved = added / 2;
if (halved % 2 === 0) {
acc.push(halved);
}
}
}
return acc;
}, []);
Mistake 4: Incorrect reduce Initial Value
// ❌ Wrong: Missing initial value
const numbers = [1, 2, 3];
const sum = numbers.reduce((acc, n) => acc + n);
// Works, but dangerous if array is empty
// ❌ Wrong: Wrong initial value type
const objects = [{ value: 1 }, { value: 2 }];
const sum = objects.reduce((acc, obj) => acc + obj.value);
// NaN - acc starts as object, not number
// ✅ Correct: Always provide initial value
const sum = numbers.reduce((acc, n) => acc + n, 0);
const sum2 = objects.reduce((acc, obj) => acc + obj.value, 0);
Mistake 5: Confusing forEach with map
// ❌ Wrong: Using forEach expecting return value
const numbers = [1, 2, 3];
const doubled = numbers.forEach(n => n * 2);
console.log(doubled); // undefined
// ✅ Correct: Use map for transformation
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
// forEach is for side effects only
numbers.forEach(n => console.log(n));
10. Performance Considerations
1. Avoid Multiple Iterations
// ❌ Slow: Multiple iterations
const numbers = [1, 2, 3, 4, 5];
const result = numbers
.map(n => n * 2) // Iteration 1
.filter(n => n > 5) // Iteration 2
.map(n => n + 1); // Iteration 3
// ✅ Fast: Single iteration with reduce
const result = numbers.reduce((acc, n) => {
const doubled = n * 2;
if (doubled > 5) {
acc.push(doubled + 1);
}
return acc;
}, []);
2. Use for Loop for Performance-Critical Code
// ❌ Slower: HOF overhead
const sum = numbers.reduce((acc, n) => acc + n, 0);
// ✅ Faster: Traditional loop
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
3. Avoid Creating Functions in Loops
// ❌ Slow: Creates new function each iteration
array.forEach((item, index) => {
setTimeout(() => console.log(item), index * 1000);
});
// ✅ Fast: Reuse function
function logItem(item) {
console.log(item);
}
array.forEach((item, index) => {
setTimeout(logItem.bind(null, item), index * 1000);
});
11. Interview Questions
Q1: What is a higher-order function?
Answer: A higher-order function is a function that either takes one or more functions as arguments, returns a function, or both. They enable functional programming patterns and code reusability.
// Takes function as argument
function execute(callback) {
return callback();
}
// Returns function
function createMultiplier(x) {
return function(y) {
return x * y;
};
}
// Both
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
Q2: What's the difference between map() and forEach()?
Answer:
- map(): Returns a new array with transformed elements
- forEach(): Returns undefined, used for side effects only
const numbers = [1, 2, 3];
// map returns new array
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
// forEach returns undefined
const result = numbers.forEach(n => n * 2);
console.log(result); // undefined
Q3: How does reduce() work?
Answer: reduce() executes a reducer function on each array element, passing the return value from the previous iteration, resulting in a single output value.
const numbers = [1, 2, 3, 4];
// Sum example
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
}, 0); // 0 is initial value
// Execution:
// acc: 0, curr: 1 → return 1
// acc: 1, curr: 2 → return 3
// acc: 3, curr: 3 → return 6
// acc: 6, curr: 4 → return 10
Q4: What is function composition?
Answer: Function composition is combining multiple functions to create a new function, where the output of one function becomes the input of the next.
const add5 = x => x + 5;
const multiply3 = x => x * 3;
// Manual composition
const result = multiply3(add5(10)); // 45
// Compose function
const compose = (f, g) => x => f(g(x));
const calculate = compose(multiply3, add5);
console.log(calculate(10)); // 45
Q5: What are pure functions and why are they important in HOFs?
Answer: Pure functions always return the same output for the same input and have no side effects. They're important because they're predictable, testable, and enable functional programming patterns.
// ✅ Pure function
const double = x => x * 2;
// ❌ Impure function (side effect)
let count = 0;
const increment = () => count++;
// ❌ Impure function (depends on external state)
const addToCount = x => x + count;
Q6: How do closures work with higher-order functions?
Answer: When a HOF returns a function, the returned function maintains access to the outer function's variables through closure, even after the outer function has returned.
function createCounter() {
let count = 0; // Captured by closure
return {
increment: () => ++count,
getCount: () => count
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
// 'count' is preserved through closure
12. Cheat Sheet
// ═══════════════════════════════════════════════════════════
// HIGHER-ORDER FUNCTIONS QUICK REFERENCE
// ═══════════════════════════════════════════════════════════
// DEFINITION
// ──────────
HOF = Function that:
• Takes function(s) as argument(s)
• Returns a function
• Or both
// COMMON ARRAY HOFS
// ─────────────────
map() → Transform each element
filter() → Select elements
reduce() → Aggregate to single value
forEach() → Execute for each (no return)
find() → Find first match
findIndex()→ Find index of first match
some() → Check if any match
every() → Check if all match
sort() → Sort with comparator
// SYNTAX PATTERNS
// ───────────────
// Callback
array.map(item => item * 2)
// Return function
const multiply = x => y => x * y
// Composition
const compose = (f, g) => x => f(g(x))
// COMMON PATTERNS
// ───────────────
// Transform
const doubled = numbers.map(n => n * 2)
// Filter
const evens = numbers.filter(n => n % 2 === 0)
// Aggregate
const sum = numbers.reduce((acc, n) => acc + n, 0)
// Chain
const result = data
.filter(x => x > 0)
.map(x => x * 2)
.reduce((acc, x) => acc + x, 0)
// BEST PRACTICES
// ──────────────
✓ Use const/let, not var
✓ Prefer pure functions
✓ Always provide initial value to reduce
✓ Use map for transformation
✓ Use filter for selection
✓ Use reduce for aggregation
✓ Avoid mutating original data
✓ Keep functions small and focused
✗ Don't overuse chaining
✗ Don't forget to return in map/filter
// PERFORMANCE
// ───────────
• Multiple iterations = slower
• Single reduce = faster
• Traditional loop = fastest
• Consider trade-off: readability vs performance
13. Summary
Key Takeaways
✅ HOF = Function operating on functions
✅ Two types: Accept functions, return functions
✅ First-class functions enable HOFs
✅ map() transforms elements
✅ filter() selects elements
✅ reduce() aggregates to single value
✅ Composition combines functions
✅ Closures preserve scope in returned functions
✅ Pure functions are predictable and testable
✅ Functional programming relies on HOFs
Best Practices
- Use map() for transformations
- Use filter() for selections
- Use reduce() for aggregations
- Provide initial values to reduce
- Avoid mutations in callbacks
- Keep functions pure when possible
- Use composition for complex operations
- Consider performance for large datasets
- Write readable code over clever code
- Test HOFs with various inputs
Higher-order functions are fundamental to modern JavaScript and enable powerful functional programming patterns. Mastering them is essential for writing clean, maintainable, and expressive code.