Partial Function Application (Áp dụng hàm một phần)
Partial function là kỹ thuật gán trước một số arguments cho hàm, trả về hàm mới nhận các arguments còn lại. Khác với Currying (chia hàm n tham số thành n hàm 1 tham số).
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Partial Function</title>
</head>
<body>
<h1>Partial Function trong JavaScript</h1>
<div class="result"></div>
<button class="btn-demo">THỬ PARTIAL FUNCTION</button>
<h3>Nhấn nút để xem partial function invocation</h3>
<script>
const resultEle = document.querySelector(".result");
const btn = document.querySelector(".btn-demo");
// Higher-order function tạo partial
function partial(fn, ...presetArgs) {
return function(...laterArgs) {
return fn(...presetArgs, ...laterArgs);
};
}
// Hàm gốc nhiều tham số
function multiply(a, b, c) {
return a * b * c;
}
btn.addEventListener("click", () => {
// Tạo partial: gán trước a=2
const double = partial(multiply, 2);
// Tạo partial: gán trước a=2, b=3
const doubleTriple = partial(multiply, 2, 3);
resultEle.textContent = "partial(multiply, 2) = " + double + "\n";
resultEle.textContent += "partial(multiply, 2)(5) = " + double(5) + " (2*5*1)\n";
resultEle.textContent += "partial(multiply, 2)(5, 3) = " + double(5, 3) + " (2*5*3)\n";
resultEle.textContent += "partial(multiply, 2, 3) = " + doubleTriple + "\n";
resultEle.textContent += "partial(multiply, 2, 3)(4) = " + doubleTriple(4) + " (2*3*4)\n";
});
</script>
</body>
</html>
Partial vs Currying - So sánh
| Đặc điểm | Partial Application | Currying |
|---|---|---|
| Định nghĩa | Gán trước MỘT SỐ args, trả về hàm nhận args còn lại | Chia hàm n args thành N hàm 1 arg |
| Gọi hàm kết quả | Có thể truyền nhiều args cùng lúc | Chỉ truyền 1 arg mỗi lần |
| Ví dụ | partial(add, 1)(2,3) → 6 | curry(add)(1)(2)(3) → 6 |
| Linh hoạt | Cao hơn | Cứng nhắc hơn |
Implementation Patterns
1. Partial đơn giản (fix first N args)
const partial = (fn, ...preset) => (...args) => fn(...preset, ...args);
// Usage
const add = (a, b, c) => a + b + c;
const add10 = partial(add, 10);
add10(5, 3); // 18
2. Partial với placeholder (lodash style)
const _ = { partial: Symbol('placeholder') };
function partialWithPlaceholder(fn, ...preset) {
return function(...args) {
const merged = preset.map(p => p === _.partial ? args.shift() : p);
return fn(...merged, ...args);
};
}
// Usage: _ là placeholder cho args sau
const greet = (greeting, name, punctuation) => `${greeting}, ${name}${punctuation}`;
const sayHello = partialWithPlaceholder(greet, 'Hello', _.partial, '!');
sayHello('World'); // "Hello, World!"
3. Bind() để partial (Built-in)
function multiply(a, b, c) {
return a * b * c;
}
const double = multiply.bind(null, 2); // a = 2
const triple = multiply.bind(null, 3); // a = 3
const doubleTriple = multiply.bind(null, 2, 3); // a=2, b=3
double(5); // 2 * 5 * undefined = NaN (cần 3 args)
// Cần: double(5, 1) hoặc dùng placeholder pattern
Ứng dụng thực tế
// 1. Configuration / Dependency Injection
function createApiClient(baseURL, timeout, headers) { ... }
const githubApi = partial(createApiClient, 'https://api.github.com', 5000);
const githubAuth = partial(githubApi, { Authorization: 'token xxx' });
// 2. Event handlers với context
function handleClick(analytics, eventName, event) {
analytics.track(eventName);
// handle UI
}
const trackClick = partial(handleClick, analytics, 'button_click');
button.addEventListener('click', trackClick);
// 3. Middleware pattern
const withLogging = (fn) => (...args) => {
console.log('Before', fn.name, args);
const result = fn(...args);
console.log('After', result);
return result;
};
const loggedAdd = withLogging(add);