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

Partial Functions (Hàm một phần) trong JavaScript

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ĩaGán trước MỘT SỐ args, trả về hàm nhận args còn lạiChia 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úcChỉ truyền 1 arg mỗi lần
Ví dụpartial(add, 1)(2,3) → 6curry(add)(1)(2)(3) → 6
Linh hoạtCao hơnCứ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);