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));