ใน 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