Functions
Higher-Order Functions
Master JavaScript Higher-Order Functions (HOFs). Learn how functions accept other functions as arguments, return functions, and enable declarative programming.
1. Introduction
In JavaScript, functions are First-Class Citizens, meaning they can be treated like any other value: assigned to variables, passed as arguments, and returned from other functions. A Higher-Order Function (HOF) is a function that accepts one or more functions as arguments, returns a function as its result, or both.
2. Why It Matters
HOFs enable a Declarative Programming style, helping you write abstract and reusable code. Instead of describing how to loop and update lists step-by-step, HOFs let you describe what transformation to apply.
3. Real-World Analogy
Think of a General Contractor:
- First-Class Function (Subcontractor): A specialized electrician or plumber. You can hire them, refer them to other projects, or bring them on site to work.
- Higher-Order Function (General Contractor): The manager who takes blueprints and hires specialized subcontractors (receives functions) to handle specific tasks (electrical, plumbing). Alternatively, the manager can assign a team leader to oversee subsequent projects (returns a new function).
4. How It Works
HOFs operate in two main patterns:
1. Accepting Functions as Arguments:
The function receives a callback wrapper to process inputs dynamically (e.g. map, filter).
2. Returning Functions:
Generates a new, customized function containing wrapped parameters inside a closure.
5. Common Built-in HOFs
| Method | Operation Description | Callback Inputs |
|---|---|---|
Array.prototype.map |
Transforms each element in an array, returning a new array. | (element, index, array) |
Array.prototype.filter |
Filters elements using a predicate check, returning a new array. | (element, index, array) |
Array.prototype.reduce |
Reduces an array to a single value using an accumulator. | (accumulator, element, index, array) |
6. Practical Example
This script demonstrates creating a custom telemetry logger HOF to measure function execution times:
7. Common Mistakes
- Forgetting to return values inside callbacks: Array methods like
mapandfilterrequire return values inside their callback functions to construct the result array correctly. - Over-complicating logic: Writing hard-to-follow, multi-layered HOF wrappers when a simple helper function would suffice.
8. Quick Quiz
Q1: Which of the following is a higher-order function?
A) Math.sqrt
B) Array.prototype.filter
Answer: B — Array.prototype.filter accepts a callback function as an argument, making it a Higher-Order Function.
9. Scenario-Based Challenge
The API Rate Limiter HOF:
You need to write a throttle wrapper throttle(fn, delay) that limits how often a function can run. The HOF should return a wrapper function that ignores executions if it has already run within the specified delay window. Outline the steps to build this.
10. Debugging Exercise
Identify and fix the scoping bug in the returned count function:
function createCounter() {
return function() {
let count = 0; // scoping issue!
count++;
return count;
};
}
const next = createCounter();
console.log(next()); // 1
console.log(next()); // 1 (Expected: 2)
View Solution
Diagnosis: The variable count is initialized inside the returned function, meaning it resets to 0 every time the function is called.
Fix: Move the variable declaration to the outer scope of the returned function to create a closure:
function createCounter() {
let count = 0; // declared in parent scope, captured in closure
return function() {
count++;
return count;
};
}
11. Interview Questions
🟢 Q1: What makes a function "first-class" in a programming language?
Answer: A language has first-class functions if it treats functions as first-class citizens. This means functions can be assigned to variables, passed as arguments to other functions, returned from other functions, and have properties and methods attached to them, just like primitive values or object instances.
12. Production Considerations
- • Avoid Callback Nesting: While chaining array methods (like
.map().filter().reduce()) is declarative, intermediate arrays are created for each step, which can cause memory overhead on large datasets. Optimize critical paths by merging loops or using single-pass operations.