Higher-Order Functions คือฟังก์ชันที่รับฟังก์ชันอื่นเป็นพารามิเตอร์ หรือคืนค่าฟังก์ชัน (หรือทั้งคู่) ใน JavaScript ฟังก์ชันเป็น First-Class Citizens จึงทำได้โดยธรรมชาติ Arrow Functions ทำให้เขียนสั้นกระชับขึ้น
แนวคิดพื้นฐาน
- First-Class Functions: ฟังก์ชันเป็นค่า (Value) ได้ - assign ให้ตัวแปร, ส่งเป็น argument, return จากฟังก์ชันอื่น
- Higher-Order Function: ฟังก์ชันที่จัดการฟังก์ชันอื่น (รับหรือคืนค่า)
- Callback: ฟังก์ชันที่ส่งเข้าไปให้ฟังก์ชันอื่นเรียกใช้ภายหลัง
ตัวอย่าง: Double Array Elements ด้วย Higher-Order Function
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Higher-Order Arrow Functions</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
.result, .sample { font-size: 18px; font-weight: 500; }
.sample { color: rebeccapurple; }
.result { color: red; }
button { padding: 8px 16px; margin: 10px 0; cursor: pointer; }
</style>
</head>
<body>
<h1>Higher-Order Arrow Functions ใน JavaScript</h1>
<div class="sample">[22, 33, 44, 55]</div>
<br>
<div class="result" id="result"></div>
<br>
<button class="Btn" id="processBtn">คูณ 2 ทุกตัวด้วย Higher-Order Function</button>
<h3>กดปุ่มด้านบนเพื่อคูณ 2 ทุก Element ของ Array ข้างต้น</h3>
<script>
const resultEle = document.getElementById("result");
const btn = document.getElementById("processBtn");
// Arrow Function: คูณ 2
const doubleNum = (ele) => ele * 2;
const arr = [22, 33, 44, 55];
// Higher-Order Function: รับ Array และ Function แล้วคืน Array ใหม่
function doubleArray(arr, fn) {
const doubleArr = [];
arr.forEach((element) => {
doubleArr.push(fn(element)); // เรียก Callback Function
});
return doubleArr;
}
// หรือเขียนสั้นด้วย map (Built-in Higher-Order Function)
const doubleArrayMap = (arr, fn) => arr.map(fn);
btn.addEventListener("click", () => {
resultEle.innerHTML = `อาร์เรย์เดิม: [${arr}]
`;
resultEle.innerHTML += `คูณ 2 (forEach): [${doubleArray(arr, doubleNum)}]
`;
resultEle.innerHTML += `คูณ 2 (map): [${doubleArrayMap(arr, doubleNum)}]`;
});
</script>
</body>
</html>
ผลลัพธ์
อาร์เรย์เดิม: [22, 33, 44, 55]
คูณ 2 (forEach): [44, 66, 88, 110]
คูณ 2 (map): [44, 66, 88, 110]
Built-in Higher-Order Functions ของ Array
| Method | คำอธิบาย | Callback Params | Return |
|---|---|---|---|
map(fn) | แปลงแต่ละ Element → Array ใหม่ | (element, index, array) | Array ใหม่ |
filter(fn) | กรอง Element ที่ผ่านเงื่อนไข | (element, index, array) | Array ใหม่ |
reduce(fn, initial) | รวมค่าทั้งหมดเป็นค่าเดียว | (acc, element, index, array) | Single Value |
forEach(fn) | วนลูปทำ Side Effect | (element, index, array) | undefined |
find(fn) | หา Element แรกที่ผ่านเงื่อนไข | (element, index, array) | Element / undefined |
some(fn) | มีอย่างน้อย 1 ตัวผ่านเงื่อนไข | (element, index, array) | Boolean |
every(fn) | ทุกตัวผ่านเงื่อนไข | (element, index, array) | Boolean |
sort(fn) | เรียงลำดับ (Mutates!) | (a, b) → number | Array เดียวกัน |
flatMap(fn) | map แล้ว flat(1) | (element, index, array) | Array ใหม่ |
การสร้าง Higher-Order Functions เอง
1. Function ที่คืนค่า Function (Currying/Factory)
// สร้าง Validator
const createValidator = (rules) => (value) => {
for (const rule of rules) {
if (!rule.test(value)) return rule.message;
}
return null; // Valid
};
const emailValidator = createValidator([
{ test: v => v.includes("@"), message: "ต้องมี @" },
{ test: v => v.length > 5, message: "สั้นเกินไป" }
]);
console.log(emailValidator("a@b")); // "สั้นเกินไป"
console.log(emailValidator("test@example.com")); // null (Valid)
2. Function Composition (รวมฟังก์ชัน)
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);
const add1 = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const compute = pipe(add1, double, square); // (x + 1) * 2 ^ 2
console.log(compute(3)); // (3+1)*2 = 8 → 8^2 = 64
3. Decorator / Wrapper (เพิ่มพฤติกรรม)
// Logger Wrapper
const withLogging = (fn) => (...args) => {
console.log(`Calling ${fn.name} with`, args);
const result = fn(...args);
console.log(`Result:`, result);
return result;
};
// Retry Wrapper
const withRetry = (fn, retries = 3) => async (...args) => {
for (let i = 0; i < retries; i++) {
try { return await fn(...args); }
catch (e) { if (i === retries - 1) throw e; }
}
};
Best Practices
- ✅ ใช้ Built-in Methods (
map,filter,reduce) แทนการเขียน Loop เอง - ✅ ใช้ Arrow Functions สำหรับ Callback สั้นๆ
- ✅ หลีกเลี่ยง Side Effects ใน
map/filter(Pure Functions) - ✅ ใช้
forEachเฉพาะ Side Effects (Logging, DOM Manipulation) - ⚠️ ห้าม Mutate Array ต้นฉบับใน Callback (ใช้
mapสร้างใหม่)