Online Compiler logoOnline Compiler

JavaScript Tutorial

JavaScript Higher-Order Functions

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.