|

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

ConceptDescription
Functions as ArgumentsYou can pass a function to another function
Returning FunctionsA function can return another function
Common HOFsmap(), filter(), reduce(), forEach(), some(), every()
Callback FunctionsFunctions passed as arguments — common in async operations
ClosuresHOFs often create closures — inner functions access outer scope
CurryingTransform multi-argument function into sequence of single-argument functions
Function CompositionCombine multiple functions to create more complex ones
HOCs in ReactFunctions that take a component and return a new component
Partial ApplicationApply some arguments to produce a function with fewer arguments
MemoizationCache 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:

FunctionOrderExample
compose(f, g, h)Right-to-leftf(g(h(x)))
pipe(f, g, h)Left-to-righth(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:

  1. withLogger takes a component (MyComponent)
  2. Returns a new component with logging functionality
  3. 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:

AspectCurryingPartial Application
TransformationN args → N functionsN args → 1 function with fewer args
Examplef(a)(b)(c)f(a, b)(c)
ResultChain of functionsSingle 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 MemoizationWith Memoization
fibonacci(40) takes ~1 secondfibonacci(40) instant
Exponential time O(2^n)Linear time O(n)
Recalculates everythingCaches 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”?

CriteriaDescription
Takes a function as argumentarr.map(fn), setTimeout(fn)
Returns a functioncreateMultiplier(2), add(5)
Bothcompose(f, g), withLogging(fn)

Common Array HOFs

MethodPurposeReturns
map()Transform each elementNew array
filter()Keep elements passing testNew array
reduce()Reduce to single valueSingle value
forEach()Execute for each elementundefined
some()Any element passes?Boolean
every()All elements pass?Boolean
find()First matching elementElement or undefined
findIndex()Index of first matchNumber
sort()Sort with comparatorSorted array

Key HOF Patterns

PatternDescriptionExample
CallbackPass function as argsetTimeout(fn, 1000)
FactoryReturn functioncreateMultiplier(2)
ClosureInner accesses outer scopecreateCounter()
CurryingN args → N functionsadd(5)(3)
CompositionCombine functionscompose(f, g)(x)
Partial ApplicationFix some argsmultiply.bind(null, 5)
MemoizationCache resultsmemoize(fibonacci)
HOC (React)Wrap componentwithLogger(MyComponent)

Currying vs Partial Application

AspectCurryingPartial Application
TransformationN args → N functionsN args → 1 function
Examplef(a)(b)(c)f(a, b)(c)
ResultChain of functionsSingle 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

PitfallProblemSolution
Overusing HOFsHarder to readUse when clearer
Unnecessary memoizationWasted memoryProfile first
Memory leaksClosures hold referencesNull out when done
this in callbacksWrong contextUse arrow functions
Deep compositionHard to debugBreak into steps

When to Use HOFs

ScenarioRecommended HOF
Transform arraymap()
Filter arrayfilter()
Aggregate arrayreduce()
Check conditionsome(), every()
Find elementfind(), findIndex()
Reusable functionFactory pattern
ConfigurationCurrying, partial application
CachingMemoization
Cross-cutting concernsHOCs, decorators
PipelinesFunction 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!