Going DeeperPro· 45 min read

Higher-Order Functions & Functional Thinking

In JavaScript, functions are values you can pass around and return. That one idea powers callbacks, map/filter/reduce, and the clean "functional" style that React leans on heavily.

What you will learn

  • Explain callbacks and higher-order functions in plain words
  • Write a function that returns another function (and use currying)
  • Apply pure functions and immutability for safer code

Functions are values

The big idea behind this whole lesson: in JavaScript a function is just a value, like a number or a string. That means you can store a function in a variable, pass it into another function, and even return a function from a function. Once you accept that, callbacks and the array methods you already use suddenly make sense.

A callback is simply a function you hand to another function so it can call it later. A higher-order function is the flip side: any function that takes a function as an argument, or returns a function. You have used these all along — map, filter and forEach are higher-order functions, and the little arrow you pass them is the callback.

repeat takes a callback and calls it
<script>
  // greet is a value we pass to repeat — that makes repeat a higher-order function
  function repeat(action, times) {
    for (let i = 1; i <= times; i++) {
      action(i);                 // call the callback, giving it the count
    }
  }

  repeat(function (n) {
    document.write("Hello #" + n + "<br>");
  }, 3);
</script>
Live preview

repeat does not know or care what the action is — it just calls whatever function you give it, three times, passing the current count. We hand it a small callback that prints a greeting. This separation ("repeat handles the looping; the callback handles what to do each time") is the heart of higher-order functions.

Note: Output: Hello #1 Hello #2 Hello #3

Watch out: Pass a function without parentheses: repeat(myFn, 3) hands over the function itself. Writing repeat(myFn(), 3) would call myFn immediately and pass its return value instead — a very common beginner mix-up.

Returning a function — and currying

Because functions are values, a function can build and return another function, tailored with some remembered setting. (This relies on closures, which you met earlier.) A function that returns another function so you can supply arguments one at a time is called currying.

A function factory: multiplyBy returns tailored functions
<script>
  // multiplyBy returns a NEW function that remembers its factor
  function multiplyBy(factor) {
    return function (n) {
      return n * factor;       // factor is remembered (closure)
    };
  }

  const double = multiplyBy(2);   // a ready-made doubling function
  const triple = multiplyBy(3);   // a ready-made tripling function

  document.write(double(5) + ", " + triple(5));
</script>
Live preview

multiplyBy(2) does not multiply anything yet — it returns a brand-new function that remembers factor = 2. We store that as double. Calling double(5) runs the returned function with the remembered factor, giving 10. triple is the same factory called with 3. One small factory produces many specialised functions — a clean, reusable pattern.

Follow how the factory builds a ready-made function step by step:

  1. multiplyBy(2) runs and returns the inner function — it does not multiply yet.
  2. That returned function remembers factor = 2 in its closure, and we store it as double.
  3. multiplyBy(3) is the same factory called again, producing a separate function that remembers factor = 3, stored as triple.
  4. Now double(5) runs the first returned function with n = 55 * 2 = 10.
  5. triple(5) runs the second returned function with n = 55 * 3 = 15.

Note: Output: 10, 15

Pure functions & immutability — the functional style

Functional programming favours pure functions. A pure function (1) always returns the same output for the same input, and (2) has no side effects — it does not change anything outside itself (no editing global variables, no changing the page, no surprises). Pure functions are easy to test, easy to reason about, and never cause spooky bugs.

A key habit is immutability: instead of changing existing data, you create a new copy with the change. Compare an impure version that mutates an array with a pure one that returns a fresh array.

Pure addPure returns a new array; the original is untouched
<script>
  const prices = [100, 200, 300];

  // IMPURE: changes (mutates) the original array — a side effect
  function addImpure(arr, value) { arr.push(value); return arr; }

  // PURE: returns a NEW array, leaving the original untouched
  function addPure(arr, value) { return [...arr, value]; }

  const result = addPure(prices, 400);
  document.write("New:      " + result.join(", ") + "<br>");
  document.write("Original: " + prices.join(", "));
</script>
Live preview

addImpure uses push, which mutates the array it was given — the caller’s original data silently changes (a side effect). addPure instead spreads the old items into a new array and adds the value, so it returns a fresh list and the original prices stays exactly as it was. The output proves the original is unchanged — that predictability is why the functional style is prized.

Note: Output: New: 100, 200, 300, 400 Original: 100, 200, 300 (The original array is untouched — addPure did not change it.)

Tip: This is exactly how React expects you to work: never mutate state directly — always produce a new value (often with spread or map). Mastering pure functions and immutability here makes React’s rules feel obvious instead of arbitrary.

Q. Which best describes a higher-order function?

Answer: A higher-order function either accepts another function as an argument (like map/filter) or returns a function (like a curried factory). This is possible because functions in JavaScript are values.

✍️ Practice

  1. Write a higher-order function applyTwice(fn, value) that returns fn(fn(value)).
  2. Write a curried greeting(word) that returns a function so greeting("Hi")("Asha") gives "Hi, Asha".
  3. Rewrite a function that mutates an array into a pure version that returns a new array.

🏠 Homework

  1. Take a small array of numbers and, using only pure operations (map, filter, reduce — no mutation), produce the sum of the squares of the even numbers.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →