Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

ฟังก์ชันที่คืนค่าฟังก์ชันอีกตัว (Function Returning Function) ใน JavaScript - Closure

ใน JavaScript ฟังก์ชันเป็น First-class Citizens สามารถสร้าง คืนค่า และส่งผ่านฟังก์ชันอื่นได้ ฟังก์ชันที่คืนค่าฟังก์ชันอีกตัว เรียกว่า Higher-Order Function และมักเกี่ยวข้องกับ Closure (การจับ Scope ภายนอกไว้)

ตัวอย่าง: ฟังก์ชันคืนค่าฟังก์ชันนับเลข (Counter)

<!DOCTYPE html>
<html lang="th">
<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", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; text-align: center; }
        .result { font-size: 48px; font-weight: bold; color: blueviolet; margin: 20px 0; font-family: monospace; }
        button { padding: 12px 24px; font-size: 18px; cursor: pointer; margin: 5px; }
        .btn-increment { background: #28a745; color: white; border: none; border-radius: 4px; }
        .btn-reset { background: #dc3545; color: white; border: none; border-radius: 4px; }
    </style>
</head>
<body>
    <h1>ฟังก์ชันคืนค่าฟังก์ชัน (Closure Counter)</h1>
    <div class="result" id="counter">0</div>
    <button class="btn-increment" id="incBtn">เพิ่ม (x2)</button>
    <button class="btn-reset" id="resetBtn">รีเซ็ต</button>
    <p>แต่ละครั้งที่กด "เพิ่ม" ค่าจะคูณ 2 (ใช้ Closure เก็บสถานะ)</p>

    <script>
        const counterDisplay = document.getElementById("counter");
        const incBtn = document.getElementById("incBtn");
        const resetBtn = document.getElementById("resetBtn");
        
        // Higher-Order Function: คืนค่าฟังก์ชันที่ "จับ" ตัวแปร b ไว้
        function createMultiplier(initialValue = 1) {
            let value = initialValue; // Private Variable (Closure)
            
            return function() {
                value *= 2;
                return value;
            };
        }
        
        // สร้าง Instance ของ Counter
        const multiplyByTwo = createMultiplier(1);
        
        incBtn.addEventListener("click", () => {
            const result = multiplyByTwo();
            counterDisplay.textContent = result;
        });
        
        resetBtn.addEventListener("click", () => {
            // ต้องสร้างใหม่เพื่อ Reset (เพราะ value ซ่อนอยู่ใน Closure)
            multiplyByTwo = createMultiplier(1);
            counterDisplay.textContent = 1;
        });
    </script>
</body>
</html>

ผลลัพธ์

เริ่มต้น: 1
กด "เพิ่ม": 2 → 4 → 8 → 16 → 32 ... (คูณ 2 ทุกครั้ง)
กด "รีเซ็ต": กลับเป็น 1

เข้าใจ Closure ผ่านตัวอย่างนี้

function createMultiplier(initialValue) {
    let value = initialValue; // 1. ตัวแปรนี้อยู่ใน Scope ของ createMultiplier
    
    return function() {       // 2. ฟังก์ชันนี้ "จับ" (Close over) ตัวแปร value ไว้
        value *= 2;           // 3. เข้าถึงและแก้ไข value ได้
        return value;
    };
}

const multiplyByTwo = createMultiplier(1);
// multiplyByTwo เป็นฟังก์ชันที่มี "Backpack" (Closure) บรรจุ value = 1

multiplyByTwo(); // 2 - ใช้ value ใน Backpack
multiplyByTwo(); // 4 - value ยังคงอยู่ใน Backpack!

รูปแบบการใช้งานทั่วไป (Patterns)

1. Factory Functions (สร้าง Object พร้อม Private State)

function createCounter(initial = 0) {
    let count = initial;
    return {
        increment: () => ++count,
        decrement: () => --count,
        getValue: () => count,
        reset: () => { count = initial; }
    };
}

const counter1 = createCounter(10);
const counter2 = createCounter(100);

counter1.increment(); // 11
counter2.increment(); // 101 (แยกจากกันสมบูรณ์)

2. Module Pattern (Encapsulation)

const UserModule = (function() {
    let users = []; // Private
    
    return {
        addUser(name) { users.push({ name, id: Date.now() }); },
        getUsers() { return [...users]; }, // คืน Copy
        count() { return users.length; }
    };
})();

UserModule.addUser("Alice");
UserModule.addUser("Bob");
console.log(UserModule.count()); // 2
// users ไม่เข้าถึงได้จากภายนอก

3. Memoization (Cache ผลลัพธ์)

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

const fibonacci = memoize(function fib(n) {
    return n <= 1 ? n : fib(n-1) + fib(n-2);
});

console.log(fibonacci(50)); // เร็วมากเพราะ Cache

4. Currying (แปลงฟังก์ชันหลาย Argument เป็น Chain)

function multiply(a) {
    return function(b) {
        return function(c) {
            return a * b * c;
        };
    };
}

// ใช้งาน
multiply(2)(3)(4); // 24
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15