JavaScript 27 🧬 Higher-order functions
Higher-order functions (HOFs) are functions that take other functions as arguments or return them as results. They’re a core concept in functional programming and allow for more flexible and reusable code.
A Quick Look at the Examples
// 1. Functions as arguments — map()
const numbers = [1, 2, 3, 4];
const doubledNumbers = numbers.map(function(number) {
return number * 2;
});
console.log(doubledNumbers); // [2, 4, 6, 8]
// 2. Returning functions — factory
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 3. Arrow functions with array methods
const squaredNumbers = numbers.map(number => number * number);
console.log(squaredNumbers); // [1, 4, 9, 16]
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // [2, 4]
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 10
// 4. Callback functions — async
setTimeout(function() {
console.log('This message is logged after 2 seconds');
}, 2000);
// 5. Closures — counter
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// 6. Currying
function add(x) {
return function(y) {
return x + y;
};
}
const add5 = add(5);
console.log(add5(3)); // 8
// 7. Function composition
function addOne(x) {
return x + 1;
}
function multiplyByTwo(x) {
return x * 2;
}
const composedFunction = (x) => multiplyByTwo(addOne(x));
console.log(composedFunction(3)); // 8
// 8. Partial application — bind
function multiply(x, y) {
return x * y;
}
const multiplyByFive = multiply.bind(null, 5);
console.log(multiplyByFive(3)); // 15
// 9. Memoization
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const memoize = (fn) => {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
};
const memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // 55
a. What is a Higher-Order Function?
Higher-order functions (HOFs) are functions that take other functions as arguments or return them as results. They’re a core concept in functional programming and allow for more flexible and reusable code.
Key Concepts
| Concept | Description |
|---|---|
| Functions as Arguments | You can pass a function to another function |
| Returning Functions | A function can return another function |
| Common HOFs | map(), filter(), reduce(), forEach(), some(), every() |
| Callback Functions | Functions passed as arguments — common in async operations |
| Closures | HOFs often create closures — inner functions access outer scope |
| Currying | Transform multi-argument function into sequence of single-argument functions |
| Function Composition | Combine multiple functions to create more complex ones |
| HOCs in React | Functions that take a component and return a new component |
| Partial Application | Apply some arguments to produce a function with fewer arguments |
| Memoization | Cache results of expensive function calls |
b. Higher-Order Function Examples — Part 1
Functions as Arguments
A function can take another function as an argument. This is a common pattern used in array methods like map(), filter(), and reduce().
// map() takes a function as argument
const doubledNumbers = numbers.map(function(number) {
return number * 2;
});
Visual:
numbers: [1, 2, 3, 4]
│
▼
.map(fn) ← Higher-order function
│
▼
[2, 4, 6, 8]
Returning Functions
A function can return another function. Useful for function factories or currying.
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
How it works:
createMultiplier(2)
│
▼
returns function (closure over multiplier = 2)
│
▼
double(5) → 5 * 2 → 10
Common Higher-Order Functions
map() — Transforms each element
const squaredNumbers = numbers.map(number => number * number);
console.log(squaredNumbers); // [1, 4, 9, 16]
filter() — Creates a new array with elements that pass a test
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(evenNumbers); // [2, 4]
reduce() — Reduces array to a single value
const sum = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0
);
console.log(sum); // 10
forEach() — Executes function for each element
numbers.forEach((number, index) => {
console.log(`Index ${index}: ${number}`);
});
some() — Returns true if any element passes
const hasEven = numbers.some(n => n % 2 === 0); // true
every() — Returns true if all elements pass
const allPositive = numbers.every(n => n > 0); // true
Callback Functions
Functions passed as arguments to other functions are called callback functions. They’re commonly used in asynchronous operations.
setTimeout(function() {
console.log('This message is logged after 2 seconds');
}, 2000);
// The function passed to setTimeout is a callback
More examples:
// Callback for array method
[1, 2, 3].forEach(n => console.log(n));
// Callback for event listener
button.addEventListener('click', () => console.log('Clicked!'));
// Callback for async operation
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data));
Closures
HOFs often create closures, where inner functions can access outer function’s scope even after the outer function has finished executing.
function createCounter() {
let count = 0; // Private variable
return function() {
count++; // Closure over `count`
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Visual:
createCounter()
│
▼
┌─────────────────┐
│ let count = 0; │ ← Retained by closure
│ │
│ return function │
│ () { │
│ count++; │ ← Accesses `count`
│ return count│
│ } │
└─────────────────┘
c. Higher-Order Function Examples — Part 2
Currying
Currying is a technique where a function with multiple arguments is transformed into a sequence of functions, each with one argument.
function add(x) {
return function(y) {
return x + y;
};
}
const add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(10)); // 15
// Arrow function version
const add2 = x => y => x + y;
console.log(add2(3)(4)); // 7
Multiple arguments curried:
const multiply = x => y => z => x * y * z;
console.log(multiply(2)(3)(4)); // 24
Practical use: reusable event handlers
const handleClick = (id) => (event) => {
console.log(`Clicked item ${id}`);
};
const handler1 = handleClick(1);
button.addEventListener('click', handler1);
Function Composition
Function composition is combining multiple functions to create more complex ones.
function addOne(x) {
return x + 1;
}
function multiplyByTwo(x) {
return x * 2;
}
// Manual composition
const composedFunction = (x) => multiplyByTwo(addOne(x));
console.log(composedFunction(3)); // 8
// Generic compose function
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);
const composed = compose(multiplyByTwo, addOne);
console.log(composed(3)); // 8 (multiplyByTwo(addOne(3)))
const piped = pipe(addOne, multiplyByTwo);
console.log(piped(3)); // 8 (same result, easier to read)
compose vs pipe:
| Function | Order | Example |
|---|---|---|
compose(f, g, h) | Right-to-left | f(g(h(x))) |
pipe(f, g, h) | Left-to-right | h(g(f(x))) |
Higher-Order Components (HOCs) in React
In React, HOCs are functions that take a component and return a new component with additional functionality.
import React from 'react';
const withLogger = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
console.log('Component did mount');
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
class MyComponent extends React.Component {
render() {
return <div>Hello, World!</div>;
}
}
export default withLogger(MyComponent);
What’s happening:
withLoggertakes a component (MyComponent)- Returns a new component with logging functionality
- The new component renders the original with the same props
Partial Application
Partial application applies some arguments to a function to produce another function with fewer arguments.
function multiply(x, y) {
return x * y;
}
// Using bind
const multiplyByFive = multiply.bind(null, 5);
console.log(multiplyByFive(3)); // 15
console.log(multiplyByFive(10)); // 50
// Using arrow function
const multiplyByTen = (y) => multiply(10, y);
console.log(multiplyByTen(3)); // 30
Difference from currying:
| Aspect | Currying | Partial Application |
|---|---|---|
| Transformation | N args → N functions | N args → 1 function with fewer args |
| Example | f(a)(b)(c) | f(a, b)(c) |
| Result | Chain of functions | Single function with some args fixed |
Memoization
Memoization is a technique for storing the results of expensive function calls and returning cached results when the same inputs occur again.
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const memoize = (fn) => {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
};
const memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // 55
console.log(memoizedFibonacci(10)); // 55 (from cache — instant!)
Why it matters:
| Without Memoization | With Memoization |
|---|---|
fibonacci(40) takes ~1 second | fibonacci(40) instant |
| Exponential time O(2^n) | Linear time O(n) |
| Recalculates everything | Caches each result |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Higher-Order Functions</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
color: #333;
line-height: 1.6;
}
h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
.demo-box {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 15px 0;
}
pre {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
}
.keyword { color: #569cd6; }
.string { color: #ce9178; }
.number { color: #b5cea8; }
.function { color: #dcdcaa; }
.comment { color: #6a9955; }
.boolean { color: #569cd6; }
#output {
background: #e9ecef;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
min-height: 40px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
border-left: 4px solid #007bff;
white-space: pre-wrap;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th { background: #007bff; color: white; }
tr:nth-child(even) { background: #f8f9fa; }
.btn {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.btn:hover {
background: #0056b3;
transform: translateY(-2px);
}
.btn-success { background: #28a745; }
.btn-success:hover { background: #1e7e34; }
.input-group {
margin: 10px 0;
}
.input-group label {
display: inline-block;
min-width: 150px;
font-weight: bold;
}
.input-group input {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1em;
width: 150px;
}
.input-group input:focus {
outline: none;
border-color: #007bff;
}
.result-display {
font-family: 'Courier New', monospace;
font-size: 1.5em;
color: #007bff;
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 15px 0;
border-left: 4px solid #007bff;
text-align: center;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Higher-Order Functions</h1>
<div class="demo-box">
<h2>1. Functions as Arguments</h2>
<pre>
<span class="keyword">const</span> numbers = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];
<span class="keyword">const</span> doubled = numbers.<span class="function">map</span>(<span class="keyword">function</span>(number) {
<span class="keyword">return</span> number * <span class="number">2</span>;
});
console.log(doubled); <span class="comment">// [2, 4, 6, 8]</span>
</pre>
</div>
<div class="demo-box">
<h2>2. Returning Functions</h2>
<pre>
<span class="keyword">function</span> <span class="function">createMultiplier</span>(multiplier) {
<span class="keyword">return</span> <span class="keyword">function</span>(number) {
<span class="keyword">return</span> number * multiplier;
};
}
<span class="keyword">const</span> double = <span class="function">createMultiplier</span>(<span class="number">2</span>);
<span class="keyword">const</span> triple = <span class="function">createMultiplier</span>(<span class="number">3</span>);
console.log(double(<span class="number">5</span>)); <span class="comment">// 10</span>
console.log(triple(<span class="number">5</span>)); <span class="comment">// 15</span>
</pre>
</div>
<div class="demo-box">
<h2>3. Common Array HOFs</h2>
<table>
<tr>
<th>Method</th>
<th>Purpose</th>
<th>Returns</th>
</tr>
<tr>
<td><code>map()</code></td>
<td>Transform each element</td>
<td>New array</td>
</tr>
<tr>
<td><code>filter()</code></td>
<td>Keep elements passing test</td>
<td>New array</td>
</tr>
<tr>
<td><code>reduce()</code></td>
<td>Reduce to single value</td>
<td>Single value</td>
</tr>
<tr>
<td><code>forEach()</code></td>
<td>Execute for each element</td>
<td>undefined</td>
</tr>
<tr>
<td><code>some()</code></td>
<td>Any element passes?</td>
<td>Boolean</td>
</tr>
<tr>
<td><code>every()</code></td>
<td>All elements pass?</td>
<td>Boolean</td>
</tr>
<tr>
<td><code>find()</code></td>
<td>First matching element</td>
<td>Element or undefined</td>
</tr>
<tr>
<td><code>findIndex()</code></td>
<td>Index of first match</td>
<td>Number</td>
</tr>
<tr>
<td><code>sort()</code></td>
<td>Sort with comparator</td>
<td>Sorted array</td>
</tr>
</table>
</div>
<div class="demo-box">
<h2>4. Currying</h2>
<pre>
<span class="comment">// Regular function</span>
<span class="keyword">function</span> <span class="function">add</span>(x) {
<span class="keyword">return</span> <span class="keyword">function</span>(y) {
<span class="keyword">return</span> x + y;
};
}
<span class="keyword">const</span> add5 = <span class="function">add</span>(<span class="number">5</span>);
console.log(add5(<span class="number">3</span>)); <span class="comment">// 8</span>
<span class="comment">// Arrow function version</span>
<span class="keyword">const</span> add2 = x => y => x + y;
console.log(add2(<span class="number">3</span>)(<span class="number">4</span>)); <span class="comment">// 7</span>
</pre>
</div>
<div class="demo-box">
<h2>5. Function Composition</h2>
<pre>
<span class="keyword">function</span> <span class="function">addOne</span>(x) { <span class="keyword">return</span> x + <span class="number">1</span>; }
<span class="keyword">function</span> <span class="function">multiplyByTwo</span>(x) { <span class="keyword">return</span> x * <span class="number">2</span>; }
<span class="comment">// Manual composition</span>
<span class="keyword">const</span> composed = (x) => <span class="function">multiplyByTwo</span>(<span class="function">addOne</span>(x));
console.log(composed(<span class="number">3</span>)); <span class="comment">// 8</span>
<span class="comment">// Generic pipe (left-to-right)</span>
<span class="keyword">const</span> pipe = (...fns) => (x) => fns.<span class="function">reduce</span>((acc, fn) => <span class="function">fn</span>(acc), x);
<span class="keyword">const</span> piped = <span class="function">pipe</span>(addOne, multiplyByTwo);
console.log(piped(<span class="number">3</span>)); <span class="comment">// 8</span>
</pre>
</div>
<div class="demo-box">
<h2>6. Partial Application</h2>
<pre>
<span class="keyword">function</span> <span class="function">multiply</span>(x, y) {
<span class="keyword">return</span> x * y;
}
<span class="keyword">const</span> multiplyByFive = multiply.<span class="function">bind</span>(<span class="keyword">null</span>, <span class="number">5</span>);
console.log(multiplyByFive(<span class="number">3</span>)); <span class="comment">// 15</span>
</pre>
</div>
<div class="demo-box">
<h2>7. Memoization</h2>
<pre>
<span class="keyword">const</span> memoize = (fn) => {
<span class="keyword">const</span> cache = {};
<span class="keyword">return</span> <span class="keyword">function</span>(...args) {
<span class="keyword">const</span> key = <span class="function">JSON.stringify</span>(args);
<span class="keyword">if</span> (cache[key]) {
<span class="keyword">return</span> cache[key];
}
<span class="keyword">const</span> result = fn.<span class="function">apply</span>(<span class="keyword">this</span>, args);
cache[key] = result;
<span class="keyword">return</span> result;
};
};
</pre>
</div>
<div class="demo-box">
<h2>8. Interactive: HOF Playground</h2>
<div class="input-group">
<label for="arrayInput">Array (comma-separated):</label>
<input type="text" id="arrayInput" value="1, 2, 3, 4, 5" style="width: 250px;">
</div>
<div style="margin: 10px 0;">
<button class="btn" onclick="runMap()">map(×2)</button>
<button class="btn" onclick="runFilter()">filter(even)</button>
<button class="btn btn-success" onclick="runReduce()">reduce(sum)</button>
<button class="btn" onclick="runSome()">some(>3)</button>
<button class="btn" onclick="runEvery()">every(>0)</button>
<button class="btn" onclick="runFind()">find(>3)</button>
</div>
<div class="result-display" id="hofResult">Result will appear here</div>
<div id="hofDetails"></div>
</div>
<div class="demo-box">
<h2>9. Interactive: Function Factory</h2>
<div class="input-group">
<label for="multiplierInput">Multiplier:</label>
<input type="number" id="multiplierInput" value="3" step="1">
</div>
<div class="input-group">
<label for="valueInput">Value to multiply:</label>
<input type="number" id="valueInput" value="7" step="1">
</div>
<button class="btn btn-success" onclick="runFactory()">Create Multiplier & Apply</button>
<div class="result-display" id="factoryResult">—</div>
<div id="factoryDetails"></div>
</div>
<div class="demo-box">
<h2>10. Interactive: Memoization Performance</h2>
<div class="input-group">
<label for="fibInput">Fibonacci number:</label>
<input type="number" id="fibInput" value="30" min="0" max="45">
</div>
<button class="btn btn-success" onclick="runMemoized()">Run Memoized</button>
<button class="btn" onclick="runUnmemoized()">Run Unmemoized</button>
<div class="result-display" id="memoResult">—</div>
<div id="memoDetails"></div>
</div>
<div class="demo-box">
<h2>11. Live Output — All Examples</h2>
<div id="output">Loading...</div>
</div>
<script>
// ============================================
// Higher-Order Functions — Live Demo
// ============================================
let results = [];
// 1. Functions as arguments
results.push('📌 Functions as Arguments:\n');
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function(number) {
return number * 2;
});
results.push(' numbers.map(n => n * 2) → [' + doubled.join(', ') + ']');
results.push('');
// 2. Returning functions
results.push('📌 Returning Functions (Factory):\n');
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
results.push(' const double = createMultiplier(2);');
results.push(' double(5) → ' + double(5));
results.push(' triple(5) → ' + triple(5));
results.push('');
// 3. Array HOFs
results.push('📌 Array Higher-Order Functions:\n');
const nums = [1, 2, 3, 4, 5];
results.push(' map(n => n * n) → [' + nums.map(n => n * n).join(', ') + ']');
results.push(' filter(n => n % 2) → [' + nums.filter(n => n % 2 === 0).join(', ') + ']');
results.push(' reduce((a,b) => a+b) → ' + nums.reduce((a, b) => a + b, 0));
results.push(' some(n => n > 4) → ' + nums.some(n => n > 4));
results.push(' every(n => n > 0) → ' + nums.every(n => n > 0));
results.push(' find(n => n > 3) → ' + nums.find(n => n > 3));
results.push('');
// 4. Closures
results.push('📌 Closures:\n');
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
results.push(' const counter = createCounter();');
results.push(' counter() → ' + counter());
results.push(' counter() → ' + counter());
results.push(' counter() → ' + counter());
results.push('');
// 5. Currying
results.push('📌 Currying:\n');
const add = x => y => x + y;
results.push(' const add = x => y => x + y;');
results.push(' add(5)(3) → ' + add(5)(3));
results.push(' add(10)(20) → ' + add(10)(20));
const add5 = add(5);
results.push(' const add5 = add(5);');
results.push(' add5(3) → ' + add5(3));
results.push(' add5(10) → ' + add5(10));
results.push('');
// 6. Function composition
results.push('📌 Function Composition:\n');
const addOne = x => x + 1;
const multiplyByTwo = x => x * 2;
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
const composed = compose(multiplyByTwo, addOne);
const piped = pipe(addOne, multiplyByTwo);
results.push(' compose(multiplyByTwo, addOne)(3) → ' + composed(3));
results.push(' pipe(addOne, multiplyByTwo)(3) → ' + piped(3));
results.push('');
// 7. Partial application
results.push('📌 Partial Application:\n');
function multiply(x, y) {
return x * y;
}
const multiplyByFive = multiply.bind(null, 5);
results.push(' const multiplyByFive = multiply.bind(null, 5);');
results.push(' multiplyByFive(3) → ' + multiplyByFive(3));
results.push(' multiplyByFive(7) → ' + multiplyByFive(7));
results.push('');
// 8. Memoization
results.push('📌 Memoization:\n');
let naiveCalls = 0;
function naiveFib(n) {
naiveCalls++;
if (n <= 1) return n;
return naiveFib(n - 1) + naiveFib(n - 2);
}
let memoCalls = 0;
function memoFib(n, cache = {}) {
memoCalls++;
if (n in cache) return cache[n];
if (n <= 1) return n;
const result = memoFib(n - 1, cache) + memoFib(n - 2, cache);
cache[n] = result;
return result;
}
const testN = 20;
naiveFib(testN);
const naiveCount = naiveCalls;
memoFib(testN);
const memoCount = memoCalls;
results.push(' Fibonacci(' + testN + '):');
results.push(' Naive: ' + naiveCount + ' function calls');
results.push(' Memoized: ' + memoCount + ' function calls');
results.push(' → ' + Math.round(naiveCount / memoCount) + 'x fewer calls!');
results.push('');
// 9. Practical: Reusable validators
results.push('📌 Practical: Reusable Validators:\n');
const isGreaterThan = (min) => (value) => value > min;
const isLessThan = (max) => (value) => value < max;
const isBetween = (min, max) => (value) => value > min && value < max;
const isAdult = isGreaterThan(17);
const isChild = isLessThan(13);
const isTeen = isBetween(12, 20);
results.push(' isAdult(20) → ' + isAdult(20));
results.push(' isAdult(15) → ' + isAdult(15));
results.push(' isChild(10) → ' + isChild(10));
results.push(' isTeen(15) → ' + isTeen(15));
results.push('');
// 10. Practical: Logger HOF
results.push('📌 Practical: Logger HOF:\n');
const withLogging = (fn) => (...args) => {
const result = fn(...args);
return result;
};
const loggedAdd = withLogging((a, b) => a + b);
results.push(' const loggedAdd = withLogging((a, b) => a + b);');
results.push(' loggedAdd(5, 3) → ' + loggedAdd(5, 3));
results.push('');
// 11. Practical: Array pipeline
results.push('📌 Practical: Array Pipeline:\n');
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = data
.filter(n => n % 2 === 0)
.map(n => n * n)
.reduce((sum, n) => sum + n, 0);
results.push(' [1..10]');
results.push(' .filter(even) → [2, 4, 6, 8, 10]');
results.push(' .map(square) → [4, 16, 36, 64, 100]');
results.push(' .reduce(sum) → ' + result);
document.getElementById('output').textContent = results.join('\n');
// ============================================
// Interactive: HOF Playground
// ============================================
function getArray() {
return document.getElementById('arrayInput').value
.split(',')
.map(n => Number(n.trim()))
.filter(n => !isNaN(n));
}
function showHof(expression, result) {
document.getElementById('hofResult').textContent =
Array.isArray(result) ? '[' + result.join(', ') + ']' : result;
document.getElementById('hofDetails').innerHTML =
`<p><code>${expression}</code></p>`;
}
function runMap() {
const arr = getArray();
showHof(`[${arr}].map(n => n * 2)`, arr.map(n => n * 2));
}
function runFilter() {
const arr = getArray();
showHof(`[${arr}].filter(n => n % 2 === 0)`, arr.filter(n => n % 2 === 0));
}
function runReduce() {
const arr = getArray();
showHof(`[${arr}].reduce((a, b) => a + b, 0)`, arr.reduce((a, b) => a + b, 0));
}
function runSome() {
const arr = getArray();
showHof(`[${arr}].some(n => n > 3)`, arr.some(n => n > 3));
}
function runEvery() {
const arr = getArray();
showHof(`[${arr}].every(n => n > 0)`, arr.every(n => n > 0));
}
function runFind() {
const arr = getArray();
showHof(`[${arr}].find(n => n > 3)`, arr.find(n => n > 3) ?? 'undefined');
}
// ============================================
// Interactive: Function Factory
// ============================================
function createMultiplierInteractive(multiplier) {
return function(number) {
return number * multiplier;
};
}
function runFactory() {
const multiplier = Number(document.getElementById('multiplierInput').value);
const value = Number(document.getElementById('valueInput').value);
const fn = createMultiplierInteractive(multiplier);
const result = fn(value);
document.getElementById('factoryResult').textContent = result;
document.getElementById('factoryDetails').innerHTML = `
<p><code>const fn = createMultiplier(${multiplier});</code></p>
<p><code>fn(${value}) → ${result}</code></p>
`;
}
// ============================================
// Interactive: Memoization
// ============================================
function getFibN() {
return Number(document.getElementById('fibInput').value);
}
function runMemoized() {
const n = getFibN();
const start = performance.now();
const memoFib = (function() {
const cache = {};
return function fib(n) {
if (n in cache) return cache[n];
if (n <= 1) return n;
const result = fib(n - 1) + fib(n - 2);
cache[n] = result;
return result;
};
})();
const result = memoFib(n);
const time = (performance.now() - start).toFixed(2);
document.getElementById('memoResult').textContent = result;
document.getElementById('memoDetails').innerHTML =
`<p><code>memoizedFib(${n}) → ${result}</code></p>
<p>Time: ${time}ms</p>
<p style="color: #28a745;">✅ Fast — cached results</p>`;
}
function runUnmemoized() {
const n = getFibN();
if (n > 35) {
document.getElementById('memoResult').textContent = '⚠️ Too slow!';
document.getElementById('memoDetails').innerHTML =
`<p style="color: #dc3545;">Try a number ≤ 35 — unmemoized Fibonacci is exponentially slow!</p>`;
return;
}
const start = performance.now();
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
const result = fib(n);
const time = (performance.now() - start).toFixed(2);
document.getElementById('memoResult').textContent = result;
document.getElementById('memoDetails').innerHTML =
`<p><code>fib(${n}) → ${result}</code></p>
<p>Time: ${time}ms</p>
<p style="color: #dc3545;">⚠️ Slow — recalculates every time</p>`;
}
// Run initial demos
runFactory();
</script>
</body>
</html>
Quick Reference
What Makes a Function “Higher-Order”?
| Criteria | Description |
|---|---|
| Takes a function as argument | arr.map(fn), setTimeout(fn) |
| Returns a function | createMultiplier(2), add(5) |
| Both | compose(f, g), withLogging(fn) |
Common Array HOFs
| Method | Purpose | Returns |
|---|---|---|
map() | Transform each element | New array |
filter() | Keep elements passing test | New array |
reduce() | Reduce to single value | Single value |
forEach() | Execute for each element | undefined |
some() | Any element passes? | Boolean |
every() | All elements pass? | Boolean |
find() | First matching element | Element or undefined |
findIndex() | Index of first match | Number |
sort() | Sort with comparator | Sorted array |
Key HOF Patterns
| Pattern | Description | Example |
|---|---|---|
| Callback | Pass function as arg | setTimeout(fn, 1000) |
| Factory | Return function | createMultiplier(2) |
| Closure | Inner accesses outer scope | createCounter() |
| Currying | N args → N functions | add(5)(3) |
| Composition | Combine functions | compose(f, g)(x) |
| Partial Application | Fix some args | multiply.bind(null, 5) |
| Memoization | Cache results | memoize(fibonacci) |
| HOC (React) | Wrap component | withLogger(MyComponent) |
Currying vs Partial Application
| Aspect | Currying | Partial Application |
|---|---|---|
| Transformation | N args → N functions | N args → 1 function |
| Example | f(a)(b)(c) | f(a, b)(c) |
| Result | Chain of functions | Single function |
Best Practices
✅ Do This:
// Use array methods instead of loops
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
// Use factories for reusable functions
const createAdder = (x) => (y) => x + y;
const add5 = createAdder(5);
// Use composition for pipelines
const processData = pipe(
filterValid,
transformData,
formatOutput
);
// Use memoization for expensive functions
const memoizedFib = memoize(fibonacci);
// Use partial application for event handlers
const handleClick = (id) => (event) => { };
// Chain HOFs for readable pipelines
const result = data
.filter(x => x > 0)
.map(x => x * 2)
.reduce((a, b) => a + b, 0);
❌ Don’t Do This:
// Don't use loops when map/filter/reduce work
const doubled = [];
for (let i = 0; i < arr.length; i++) {
doubled.push(arr[i] * 2); // ❌ Use .map()
}
// Don't over-compose (keep it readable)
const result = compose(f, g, h, i, j, k, l, m)(x); // ❌ Too much
// Don't memoize everything
const memoized = memoize((a, b) => a + b); // ⚠️ Probably unnecessary
// Don't ignore closures (they hold memory)
function outer() {
const bigData = new Array(1000000);
return () => bigData.length; // ⚠️ Holds bigData forever
}
// Don't confuse currying with partial application
// Currying: f(a)(b)(c)
// Partial: f(a, b)(c)
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Overusing HOFs | Harder to read | Use when clearer |
| Unnecessary memoization | Wasted memory | Profile first |
| Memory leaks | Closures hold references | Null out when done |
this in callbacks | Wrong context | Use arrow functions |
| Deep composition | Hard to debug | Break into steps |
When to Use HOFs
| Scenario | Recommended HOF |
|---|---|
| Transform array | map() |
| Filter array | filter() |
| Aggregate array | reduce() |
| Check condition | some(), every() |
| Find element | find(), findIndex() |
| Reusable function | Factory pattern |
| Configuration | Currying, partial application |
| Caching | Memoization |
| Cross-cutting concerns | HOCs, decorators |
| Pipelines | Function composition |
Pro Tip: Higher-order functions are the superpower of JavaScript. Use map, filter, and reduce instead of loops for cleaner, more expressive array operations. Use factories (createMultiplier) to generate reusable functions. Use currying (add(5)(3)) for configuration. Use composition (pipe(f, g, h)) for data pipelines. Use memoization for expensive recursive functions. And remember: React HOCs, Redux middleware, and Express middleware are all higher-order functions in action!
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!