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.mapiterates, but it delegates the transformation to your callback.setTimeoutwaits, but it delegates what-to-do-when-done to your callback.In this lesson you'll rebuild
map,filter, andreducefrom scratch, and you'll write your own HOFs that return new specialized functions.Once you can implement
filterin five lines, no interviewer can scare you with a "write your ownmap" question.Read the full lesson -> [link]
#JavaScript #InterviewPrep #HigherOrderFunctions #MapFilterReduce #Callbacks #FunctionalProgramming #CodingInterview
What You'll Learn
- The two-part definition of a higher-order function
- How to rebuild
map,filter, andreducefrom 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:
- Takes one or more functions as arguments
- 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
Common Mistakes
- Writing
arr.map(fn())instead ofarr.map(fn)— the first callsfnimmediately and passes its return value as the callback. HOFs need the function reference, not its result. - Forgetting that
mapreturns a new array whileforEachreturnsundefined— if you need the transformed data, usemap; if you only need side effects, useforEach. You cannot chain afterforEach. - Using
reducewith no initial value on a potentially empty array — it throwsTypeError: 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, andaddEventListener.
Q: Implement Array.prototype.map from scratch.
See the
myMapimplementation 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
myFilterabove. 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?
reducecollapses an array to a single value. It takes a reducer callback(accumulator, currentValue, index, array) => newAccumulatorand 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?
mapreturns a new array with transformed values.forEachreturnsundefined— it's used for side effects (logging, DOM manipulation). You can't chain afterforEach, but you can chain aftermap.
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(alsoforEach,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.