Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Hàm trả về hàm khác (Function Returning Function) trong JavaScript

Khái niệm

Hàm bậc cao (Higher-Order Function) - hàm nhận hàm làm tham số hoặc trả về hàm. Đây là cơ sở của Closure và Functional Programming.

Ví dụ: Counter với Closure

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Function Returning Function</title>
    <style>
        body { font-family: 'Segoe UI', sans-serif; padding: 20px; text-align: center; }
        .result { font-size: 48px; font-weight: bold; color: #6a1b9a; margin: 30px 0; font-family: monospace; }
        button { padding: 15px 30px; font-size: 18px; background: #1976d2; color: white; border: none; border-radius: 8px; cursor: pointer; margin: 5px; }
        button:hover { background: #1565c0; }
        .controls { margin: 20px 0; }
    </style>
</head>
<body>
    <h1>Hàm trả về hàm - Closure Counter</h1>
    <div class="result" id="result">0</div>
    <div class="controls">
        <button id="btnIncrement">TĂNG (x2)</button>
        <button id="btnReset">RESET</button>
    </div>
    <h3>Mỗi lần nhấn TĂNG: số nhân đôi (closure giữ state)</h3>

    <script>
        const resultEle = document.getElementById('result');
        const btnInc = document.getElementById('btnIncrement');
        const btnReset = document.getElementById('btnReset');

        // Higher-order function: trả về function khác
        function createMultiplier(initialValue = 1) {
            let value = initialValue; // Biến private (closure)
            
            // Trả về function - có quyền truy cập 'value'
            return function() {
                value *= 2;
                return value;
            };
        }

        // Tạo instance counter
        const doubleCounter = createMultiplier(1);
        
        // Reset function (cũng dùng closure)
        function createResettableCounter(initialValue = 1) {
            let value = initialValue;
            return {
                increment: () => (value *= 2),
                decrement: () => (value = Math.max(1, value / 2)),
                getValue: () => value,
                reset: () => (value = initialValue)
            };
        }
        
        const counter = createResettableCounter(1);
        
        function updateDisplay() {
            resultEle.textContent = counter.getValue();
        }

        btnInc.addEventListener('click', () => {
            counter.increment();
            updateDisplay();
        });
        
        btnReset.addEventListener('click', () => {
            counter.reset();
            updateDisplay();
        });
        
        updateDisplay();
    </script>
</body>
</html>

Kết quả

Nhấn "TĂNG": 1 → 2 → 4 → 8 → 16 → 32...
Nhấn "RESET": Về 1

Các pattern phổ biến

1. Factory Function (Tạo object có private state)

function createUser(name) {
    let _password = ''; // Private
    
    return {
        getName: () => name,
        setPassword: (pwd) => { _password = pwd; },
        checkPassword: (pwd) => _password === pwd
    };
}

const user = createUser('Minh');
user.setPassword('123');
console.log(user.checkPassword('123')); // true
console.log(user._password); // undefined - không truy cập được

2. Currying (Curry function)

// Curry: f(a,b,c) -> f(a)(b)(c)
const multiply = (a) => (b) => (c) => a * b * c;

const double = multiply(2);      // (b) => (c) => 2 * b * c
const doubleTriple = double(3);  // (c) => 6 * c
console.log(doubleTriple(4));    // 24

// Ứng dụng: Cấu hình linh hoạt
const fetchWithAuth = (token) => (url) => (options) => 
    fetch(url, { ...options, headers: { Authorization: `Bearer ${token}` } });

const apiCall = fetchWithAuth('my-token');
const getUsers = apiCall('/api/users');
const getPosts = apiCall('/api/posts');

3. Memoization (Cache kết quả)

function memoize(fn) {
    const cache = new Map();
    return (...args) => {
        const key = JSON.stringify(args);
        if (cache.has(key)) return cache.get(key);
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}

const fibonacci = memoize(n => n <= 1 ? n : fibonacci(n-1) + fibonacci(n-2));
console.log(fibonacci(50)); // Nhanh nhờ cache

4. Middleware Pattern (Express.js style)

function logger(message) {
    return (next) => (...args) => {
        console.log(`[LOG] ${message}`, args);
        return next(...args);
    };
}

function timer(fn) {
    return (...args) => {
        const start = performance.now();
        const result = fn(...args);
        console.log(`⏱ ${fn.name}: ${(performance.now() - start).toFixed(2)}ms`);
        return result;
    };
}

// Compose
const enhancedFn = logger('CALL')(timer(originalFn));