Problem Statement
What are higher-order functions? Provide examples and explain why they are useful.
Explanation
A higher-order function is a function that either takes one or more functions as arguments, or returns a function, or both.
Taking functions as arguments:
Array methods like map, filter, reduce, forEach are higher-order functions. Event listeners take callback functions. setTimeout and setInterval take functions. Custom functions can accept functions to make them reusable.
Returning functions:
Create specialized functions from generic ones. Implement currying and partial application. Build function factories. Create closures for data privacy.
Why they are useful:
Code reuse by passing different behaviors. Abstraction of common patterns. Functional programming style. More flexible and composable code. Easier to test and maintain.
Common higher-order function patterns:
Map for transforming arrays. Filter for selecting elements. Reduce for aggregating data. Compose for combining functions. Memoize for caching. Throttle and debounce for rate limiting.
Benefits:
DRY principle, do not repeat yourself. Declarative rather than imperative code. Easier to reason about. Better separation of concerns.
Higher-order functions are fundamental to functional programming and modern JavaScript. They enable powerful patterns and cleaner code. Understanding them is essential for advanced JavaScript development.
Code Solution
SolutionRead Only
// Array methods are higher-order functions
const numbers = [1, 2, 3, 4, 5];
// map - takes function, returns new array
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - takes function, returns filtered array
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce - takes function, returns single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15
// Creating higher-order function
function repeat(times, action) {
for (let i = 0; i < times; i++) {
action(i);
}
}
repeat(3, console.log); // 0, 1, 2
// Returning a function
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Practical example - once function
function once(fn) {
let called = false;
let result;
return function(...args) {
if (!called) {
called = true;
result = fn(...args);
}
return result;
};
}
const initialize = once(() => {
console.log('Initialized!');
return 'Ready';
});
console.log(initialize()); // 'Initialized!' 'Ready'
console.log(initialize()); // 'Ready' (no log)
// Compose functions
function compose(...fns) {
return function(value) {
return fns.reduceRight((acc, fn) => fn(acc), value);
};
}
const add5 = n => n + 5;
const multiply3 = n => n * 3;
const subtract2 = n => n - 2;
const calculate = compose(subtract2, multiply3, add5);
console.log(calculate(10)); // ((10 + 5) * 3) - 2 = 43
// Debounce (higher-order function)
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
const search = debounce((query) => {
console.log('Searching:', query);
}, 500);
// Memoization
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}