JavaScript Interview Prep
Functions Deep Dive

Higher-Order Functions

Functions That Manage Functions

LinkedIn Hook

map, filter, reduce, setTimeout, addEventListener — they all have one thing in common.

They're higher-order functions. They either take a function as an argument, or return a function, or both. And once you see the pattern, you see it everywhere: Redux middleware, React hooks, Express handlers, debounce, throttle, memoize.

A higher-order function is a manager. It doesn't do the grunt work — it delegates. Array.map iterates, but it delegates the transformation to your callback. setTimeout waits, but it delegates what-to-do-when-done to your callback.

In this lesson you'll rebuild map, filter, and reduce from scratch, and you'll write your own HOFs that return new specialized functions.

Once you can implement filter in five lines, no interviewer can scare you with a "write your own map" question.

Read the full lesson -> [link]

#JavaScript #InterviewPrep #HigherOrderFunctions #MapFilterReduce #Callbacks #FunctionalProgramming #CodingInterview


Higher-Order Functions thumbnail


What You'll Learn

  • The two-part definition of a higher-order function
  • How to rebuild map, filter, and reduce from scratch
  • How to write HOFs that return new specialized functions (like once)

What Is a Higher-Order Function?

A higher-order function (HOF) is any function that does at least one of these:

  1. Takes one or more functions as arguments
  2. Returns a function as its result

You've already seen both patterns. executeOperation from the previous lesson takes a function. createMultiplier returns a function. Both are higher-order functions.

Think of a higher-order function as a manager. A manager doesn't do the work directly — they delegate. They take a worker (function), tell them what to do, and coordinate the result. Array.map is a manager: it iterates the array, but delegates the transformation to the callback you provide.

Built-in HOFs: map, filter, reduce

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map — transform each element
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

// filter — keep elements that pass a test
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]

// reduce — accumulate into a single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 55

Build Your Own map from Scratch

function myMap(array, transformFn) {
  const result = [];
  for (let i = 0; i < array.length; i++) {
    result.push(transformFn(array[i], i, array));
  }
  return result;
}

const nums = [1, 2, 3, 4];
console.log(myMap(nums, x => x * 3));        // [3, 6, 9, 12]
console.log(myMap(nums, (x, i) => x + i));   // [1, 3, 5, 7]

// As a prototype method
Array.prototype.myMap = function(transformFn) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    result.push(transformFn(this[i], i, this));
  }
  return result;
};

console.log([10, 20, 30].myMap(x => x / 10)); // [1, 2, 3]

Build Your Own filter from Scratch

function myFilter(array, predicateFn) {
  const result = [];
  for (let i = 0; i < array.length; i++) {
    if (predicateFn(array[i], i, array)) {
      result.push(array[i]);
    }
  }
  return result;
}

console.log(myFilter([1, 2, 3, 4, 5], x => x > 3)); // [4, 5]

// As a prototype method
Array.prototype.myFilter = function(predicateFn) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (predicateFn(this[i], i, this)) {
      result.push(this[i]);
    }
  }
  return result;
};

console.log([10, 15, 20, 25].myFilter(x => x >= 20)); // [20, 25]

Build Your Own reduce from Scratch

function myReduce(array, reducerFn, initialValue) {
  let accumulator = initialValue;
  let startIndex = 0;

  // If no initial value, use first element
  if (accumulator === undefined) {
    accumulator = array[0];
    startIndex = 1;
  }

  for (let i = startIndex; i < array.length; i++) {
    accumulator = reducerFn(accumulator, array[i], i, array);
  }

  return accumulator;
}

console.log(myReduce([1, 2, 3, 4], (acc, val) => acc + val, 0));    // 10
console.log(myReduce([1, 2, 3, 4], (acc, val) => acc * val, 1));    // 24
console.log(myReduce([1, 2, 3, 4], (acc, val) => acc + val));       // 10 (no initial value)

// Flatten an array with reduce
console.log(myReduce([[1, 2], [3, 4], [5]], (acc, val) => acc.concat(val), []));
// [1, 2, 3, 4, 5]

Custom Higher-Order Function: Repeat and Once

function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}

repeat(3, i => console.log("Iteration " + i));
// Iteration 0
// Iteration 1
// Iteration 2

// HOF that returns a function — rate limiter
function once(fn) {
  let called = false;
  let result;
  return function(...args) {
    if (!called) {
      called = true;
      result = fn.apply(this, args);
    }
    return result;
  };
}

const initialize = once(() => {
  console.log("Initialized!");
  return 42;
});

console.log(initialize()); // "Initialized!" -> 42
console.log(initialize()); // 42 (function not called again)
console.log(initialize()); // 42

Higher-Order Functions visual 1


Common Mistakes

  • Writing arr.map(fn()) instead of arr.map(fn) — the first calls fn immediately and passes its return value as the callback. HOFs need the function reference, not its result.
  • Forgetting that map returns a new array while forEach returns undefined — if you need the transformed data, use map; if you only need side effects, use forEach. You cannot chain after forEach.
  • Using reduce with no initial value on a potentially empty array — it throws TypeError: Reduce of empty array with no initial value. Always pass an explicit initial value when the input might be empty.

Interview Questions

Q: What is a higher-order function?

A higher-order function is a function that either takes one or more functions as arguments, or returns a function, or both. Examples include Array.prototype.map, filter, reduce, setTimeout, and addEventListener.

Q: Implement Array.prototype.map from scratch.

See the myMap implementation above. Key points: iterate the array, call the callback with (element, index, array), push each result into a new array, and return the new array without mutating the original.

Q: Implement Array.prototype.filter from scratch.

See myFilter above. Iterate the array, call the predicate with (element, index, array); if it returns truthy, push the element into a new array. Return the new array. Do not mutate the input.

Q: What does Array.prototype.reduce do? What are its parameters?

reduce collapses an array to a single value. It takes a reducer callback (accumulator, currentValue, index, array) => newAccumulator and an optional initial accumulator. If no initial value is given, the first array element becomes the initial accumulator and iteration starts at index 1.

Q: What is the difference between map and forEach?

map returns a new array with transformed values. forEach returns undefined — it's used for side effects (logging, DOM manipulation). You can't chain after forEach, but you can chain after map.

Q: What is a higher-order function? Give 3 built-in examples.

A function that takes or returns a function. Built-in examples: Array.prototype.map, Array.prototype.filter, Array.prototype.reduce (also forEach, setTimeout, addEventListener, Function.prototype.bind).


Quick Reference — Cheat Sheet

HIGHER-ORDER FUNCTIONS — QUICK MAP

Definition:
  Takes a fn as arg, OR returns a fn, OR both.

Built-in HOFs:
  Array:       map, filter, reduce, forEach, some, every, sort, find
  Function:    bind, call, apply
  Timing:      setTimeout, setInterval
  Events:      addEventListener

Signatures of the big three:
  map(cb)              -> new array, same length
  filter(predicate)    -> new array, subset
  reduce(cb, init?)    -> single value

Reduce callback:
  (accumulator, currentValue, index, array) => newAccumulator

HOFs that return functions:
  once(fn), memoize(fn), debounce(fn, ms), throttle(fn, ms)

Rule of thumb:
  map       -> transform
  filter    -> select
  reduce    -> accumulate
  forEach   -> side effects only

Previous: First-Class Functions Next: IIFE -> Run Once, Vanish Forever


This is Lesson 6.2 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page