JavaScript
Modern Data Syntax
Rest Parameters
เรียนรู้ Rest Parameters (...) — syntax สำหรับรวบ argument, element, หรือ property ที่เหลือเข้าเป็น array/object ใช้ใน function parameter, array destructuring และ object destructuring
Rest Parameters คืออะไร — อีกด้านของ ...
ในบท Spread Operator เราเรียนว่า `...` ใช้ **กระจาย** ของออก — `[...arr]` กระจาย array ออกเป็น element แยก ๆ Rest Parameters คือการใช้ `...` ตรงข้าม — ใช้ **รวบ** ค่าหลาย ๆ ค่าเข้าเป็น array กฎง่าย ๆ แยก Spread กับ Rest: - **Spread** = ใช้ทางขวาของ `=` — แกะของออก (`const copy = [...arr]`) - **Rest** = ใช้ทางซ้ายของ `=` — รวบของเข้า (`const [a, ...rest] = arr`) Rest ใช้งานได้ 3 ที่: 1. **Function parameter** — รวบ argument ที่ส่งเข้า function 2. **Array destructuring** — รวบ element ที่เหลือใน array 3. **Object destructuring** — รวบ property ที่เหลือใน object
// Spread — ทางขวาของ = : กระจาย array ออก
const nums = [1, 2, 3];
const copy = [...nums]; // copy = [1, 2, 3] — แกะ nums ออก
// Rest — ทางซ้ายของ = : รวบค่าเข้า array
const [first, ...rest] = [1, 2, 3];
console.log(first); // 1 — ค่าแรก
console.log(rest); // [2, 3] — รวบที่เหลือเข้า arrayเราเคยใช้ rest ใน array destructuring มาแล้ว จากบท Destructuring Array — `const [head, ...tail] = arr` คือ rest pattern ตอนนี้เราจะเรียน rest ในมุมกว้าง: function parameter, object destructuring และเข้าใจกฎการใช้งานทั้งหมด
Rest ใน Function Parameter — รับกี่ argument ก็ได้
ใช้ `...` นำหน้าชื่อ parameter **ตัวสุดท้าย** ของ function เพื่อรวบ argument ทั้งหมดที่ส่งเข้ามาให้กลายเป็น array — ทำให้ function รับ argument กี่ตัวก็ได้โดยไม่ต้องประกาศ parameter ตายตัว ก่อนมี Rest เราต้องใช้ `arguments` object (คล้าย array แต่ไม่ใช่ array จริง) — Rest ได้ array ของแท้ ทำให้ใช้ array method ได้ทันที
// ...nums รวบทุก argument เป็น array
function sum(...nums) {
console.log("nums เป็น array?", Array.isArray(nums)); // true
return nums.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
console.log(sum()); // 0 (reduce กับ array ว่าง ได้ initial 0)
// เทียบกับแบบเก่า — arguments object ไม่ใช่ array
function oldSum() {
// arguments เป็น array-like — ใช้ map/reduce ไม่ได้
console.log(Array.isArray(arguments)); // false
}**ข้อดีของ Rest เหนือ `arguments` object**: - ได้ **array ของแท้** — ใช้ `.map()`, `.filter()`, `.reduce()` ได้ทันที - ชื่อ parameter ชัดเจน — `...scores` สื่อความหมายกว่า `arguments[0]` - ทำงานได้ใน arrow function (`arguments` ใช้ใน arrow function ไม่ได้) - เลือกได้ว่าจะรวบทั้งหมด (`...args`) หรือแค่ที่เหลือ (`a, b, ...rest`)
Rest ร่วมกับ Parameter อื่น — เก็บเฉพาะที่เหลือ
Rest ไม่ต้องรวบทุก argument ก็ได้ — วาง parameter ธรรมดาก่อน แล้วใช้ rest parameter **ตัวสุดท้าย** จะรวบเฉพาะ argument ที่เหลือ **กฎสำคัญ**: Rest parameter ต้องเป็น parameter ตัวสุดท้ายเสมอ — `function fn(...a, b)` เขียนไม่ได้ เพราะ JavaScript จะไม่รู้ว่า `b` ควรได้รับ argument ไหน
// first กับ second รับ argument 2 ตัวแรก — rest รับที่เหลือ
function logAll(first, second, ...rest) {
console.log("first:", first);
console.log("second:", second);
console.log("rest:", rest);
}
logAll("a", "b", "c", "d", "e");
// first: a
// second: b
// rest: ['c', 'd', 'e']
logAll(1, 2);
// first: 1
// second: 2
// rest: [] — ไม่มี argument เหลือ → array ว่าง
// ใช้ในทางปฏิบัติ — function ที่รับ config แล้วตามด้วย items
function addToList(listName, ...items) {
console.log("เพิ่มเข้า " + listName + ": " + items.join(", "));
}
addToList("ผลไม้", "แอปเปิ้ล", "กล้วย", "ส้ม");
// เพิ่มเข้า ผลไม้: แอปเปิ้ล, กล้วย, ส้ม**สำคัญ**: ถ้า rest รับ argument ไม่ได้ (ไม่มีเหลือ) — rest จะเป็น **array ว่าง (`[]`)** ไม่ใช่ `undefined` — ทำให้เรียก method ต่อได้โดยไม่ต้องเช็กก่อน
Rest ใน Array Destructuring
เราเห็น rest ใน array destructuring มาก่อนแล้ว: `const [first, ...rest] = arr` — เป็น pattern ที่ใช้ "แยกหัว" (head) กับ "หาง" (tail) ออกจากกัน ใช้ใน recursive algorithm และ functional programming บ่อยมาก
const numbers = [10, 20, 30, 40, 50];
// head = element แรก, tail = ที่เหลือ
const [head, ...tail] = numbers;
console.log(head); // 10
console.log(tail); // [20, 30, 40, 50]
// ใช้กับ array ซ้อน — skip บางตำแหน่งแล้ว rest
const data = ["id:001", "สมชาย", "กรุงเทพ", "เชียงใหม่", "ภูเก็ต"];
const [id, name, ...cities] = data;
console.log(id); // "id:001"
console.log(name); // "สมชาย"
console.log(cities); // ["กรุงเทพ", "เชียงใหม่", "ภูเก็ต"]
// rest ต้องเป็นตัวสุดท้าย — [a, ...rest, b] ใช้ไม่ได้!
**ข้อควรระวัง**: Rest ต้องอยู่ตำแหน่งสุดท้ายใน destructuring pattern — `[a, ...rest, b]` จะเกิด syntax error เพราะ `...rest` กินทุก element จนหมด `b` จึงไม่มีทางได้ค่า
Rest ใน Object Destructuring
Rest ยังใช้ใน object destructuring ได้ — ดึงบาง property ออก แล้วรวบ property ที่เหลือเข้า object ใหม่ มีประโยชน์เมื่ออยากแยก field ที่สนใจออกจาก field อื่น ๆ ทั้งหมด (เช่น แยก id กับ name ออกจากข้อมูลที่เหลือ)
const user = {
id: 1001,
name: "สมชาย",
age: 25,
city: "กรุงเทพ",
role: "developer",
email: "somchai@email.com",
};
// ดึง id กับ name — ที่เหลือเก็บใน rest
const { id, name, ...rest } = user;
console.log(id); // 1001
console.log(name); // "สมชาย"
console.log(rest);
// { age: 25, city: "กรุงเทพ", role: "developer", email: "somchai@email.com" }
// ใช้กับ API response — แยก field สำคัญออกจาก metadata
const response = {
success: true,
data: { items: [1, 2, 3] },
timestamp: "2024-01-01",
requestId: "abc-123",
};
const { success, data, ...meta } = response;
console.log(success); // true
console.log(data); // { items: [1, 2, 3] }
console.log(meta); // { timestamp: "2024-01-01", requestId: "abc-123" }**ข้อดีของ rest ใน object destructuring**: - แยก field ที่สนใจออกจาก field อื่นโดยไม่ต้องระบุชื่อ field ทั้งหมด - มีประโยชน์มากกับ API response, props forwarding ใน React, หรือการ filter บาง field ออก - `rest` จะเป็น **object ใหม่** — การแก้ไข `rest` ไม่กระทบ object ต้นฉบับ (แต่ nested object ยัง share reference — shallow copy เช่นเดียวกับ spread)
กฎสำคัญและข้อควรระวัง
| Syntax | ความหมาย | ข้อควรระวัง |
|---|---|---|
| `function fn(...args)` | รวบทุก argument เป็น array ชื่อ args | args เป็น array ของแท้ ใช้ .map .filter ได้ทันที |
| `function fn(a, b, ...rest)` | เก็บ argument แรกใน a, b — ที่เหลือใน rest | rest ต้องเป็น parameter ตัวสุดท้าย — `fn(...a, b)` ใช้ไม่ได้ |
| `const [a, ...rest] = arr` | ดึง element แรก — ที่เหลือเป็น array rest | ถ้า arr มีแค่ 1 element — rest เป็น array ว่าง `[]` |
| `const { a, ...rest } = obj` | ดึง property a — ที่เหลือเป็น object rest | rest เป็น object ใหม่ (shallow copy) — nested object ยัง share reference |
| Rest ได้ array/object ว่างเสมอเมื่อไม่มีเหลือ | ไม่มี argument/element เหลือ → `[]` หรือ `{}` | ไม่ใช่ `undefined` — เรียก method ต่อได้เลย |
| Rest ต้องอยู่ตำแหน่งสุดท้าย | `fn(a, ...rest)` ✓, `fn(...rest, a)` ✗ | มี rest เดียวได้ — `fn(...a, ...b)` ใช้ไม่ได้ |
Rest Parameters ทำให้ JavaScript function ยืดหยุ่นขึ้นมาก — จากที่ต้องรู้จำนวน argument ล่วงหน้า กลายเป็นรับกี่ตัวก็ได้โดย function ทำงานได้ถูกต้องเหมือนเดิม