JavaScript Tutorial
Master higher-order functions for functional programming and powerful abstractions.
Why this matters
Higher-order functions are central to functional programming in JavaScript. They enable powerful patterns and make code more modular, reusable, and expressive.
What are Higher-Order Functions?
A higher-order function is a function that operates on other functions. It either takes one or more functions as arguments, returns a function, or both.
Higher-order functions are a cornerstone of functional programming and enable powerful abstraction patterns.
Higher-Order Function Basics
// Takes a function as argument
function execute(fn) {
return fn();
}
const sayHello = () => "Hello!";
console.log(execute(sayHello)); // Hello!
// Returns a function
function createMultiplier(x) {
return function(y) {
return x * y;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Both takes and returns functions
function compose(fn1, fn2) {
return function(x) {
return fn1(fn2(x));
};
}
const addTwo = (x) => x + 2;
const multiplyByThree = (x) => x * 3;
const combined = compose(addTwo, multiplyByThree);
console.log(combined(5)); // 17 (5*3 = 15, 15+2 = 17)
Higher-order functions treat functions as first-class values.
Built-in Higher-Order Functions
Many JavaScript methods are higher-order functions. Array methods like map, filter, reduce are perfect examples.
Array Methods
const numbers = [1, 2, 3, 4, 5];
// map - transform each element
const squared = numbers.map((n) => n * n);
console.log(squared); // [1, 4, 9, 16, 25]
// filter - select elements
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce - combine all elements
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15
// find - return first match
const firstEven = numbers.find((n) => n % 2 === 0);
console.log(firstEven); // 2
// findIndex - return index of first match
const idx = numbers.findIndex((n) => n > 3);
console.log(idx); // 3 (index of value 4)
// every - check if all match
const allPositive = numbers.every((n) => n > 0);
console.log(allPositive); // true
// some - check if any match
const hasNegative = numbers.some((n) => n < 0);
console.log(hasNegative); // false
Array methods accept callbacks and are higher-order functions.
Function Returning Functions
Functions that return other functions enable powerful patterns like currying and partial application.
Returning Functions
// Simple function factory
function createGreeter(greeting) {
return function(name) {
return greeting + ", " + name + "!";
};
}
const hello = createGreeter("Hello");
const goodbye = createGreeter("Goodbye");
console.log(hello("Alice")); // Hello, Alice!
console.log(goodbye("Bob")); // Goodbye, Bob!
// Currying - converting a multi-argument function
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...nextArgs) => curried(...args, ...nextArgs);
}
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
// Decorators - wrapping functions
function withLogging(fn) {
return function(...args) {
console.log("Calling with arguments:", args);
const result = fn(...args);
console.log("Result:", result);
return result;
};
}
const addLog = withLogging((a, b) => a + b);
addLog(5, 3);
// Calling with arguments: [5, 3]
// Result: 8
// 8
Returning functions enables currying, partial application, and decorators.
Function Composition
Function composition combines multiple functions into a new function, enabling powerful abstraction.
Composing Functions
// Helper functions
const double = (x) => x * 2;
const addTen = (x) => x + 10;
const square = (x) => x * x;
// Manual composition
const result1 = square(addTen(double(5)));
console.log(result1); // 400 ((5*2+10)^2 = 20^2 = 400)
// Compose function (right to left)
function compose(...fns) {
return (x) => fns.reduceRight((acc, fn) => fn(acc), x);
}
const composed = compose(square, addTen, double);
console.log(composed(5)); // 400
// Pipe function (left to right)
function pipe(...fns) {
return (x) => fns.reduce((acc, fn) => fn(acc), x);
}
const piped = pipe(double, addTen, square);
console.log(piped(5)); // 400
// Using with promises
function fetchUser(id) {
return Promise.resolve({ id, name: "Alice" });
}
function formatUser(user) {
return "User: " + user.name;
}
fetchUser(1)
.then(formatUser)
.then(console.log);
// User: Alice
Composition creates new functions from existing ones.
Practical Examples
Higher-order functions enable cleaner, more modular code for common programming patterns.
Real-World Uses
// Memoization - cache function results
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) {
console.log("Cache hit!");
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
const expensiveCalc = memoize((n) => {
console.log("Calculating...");
return n * n;
});
console.log(expensiveCalc(5)); // Calculating... 25
console.log(expensiveCalc(5)); // Cache hit! 25
// Timing wrapper
function withTiming(fn) {
return function(...args) {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log("Execution time: " + (end - start).toFixed(2) + "ms");
return result;
};
}
const slowFunc = withTiming(() => {
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
return sum;
});
slowFunc();
// Execution time: X.XXms
Higher-order functions create useful patterns like memoization and timing.
Common Mistakes
- Forgetting to return a function: Higher-order functions that create functions must explicitly return the new function.
- Confusing function composition order: In compose (right-to-left), functions are applied from right to left. In pipe (left-to-right), functions are applied left to right.
- Not understanding closure: Higher-order functions rely heavily on closures. The returned function has access to parent function's variables.
FAQ
What is a higher-order function?
A higher-order function is a function that takes one or more functions as arguments and/or returns a function.
Why use higher-order functions?
They enable code reuse, abstraction, and functional programming patterns like composition, currying, and memoization.
Is map() a higher-order function?
Yes, map() is a higher-order function because it takes a callback function as an argument.
What is function composition?
Function composition combines multiple functions into a single function by passing the output of one function as input to the next.
Related Topics