JavaScript
Modern Data Syntax
Spread Operator
เรียนรู้ Spread Operator (...) — syntax สำหรับกระจาย array และ object เพื่อ copy, รวม, override และส่งเป็น function argument พร้อมเข้าใจข้อจำกัดของ shallow copy
Spread Operator คืออะไร
Spread operator คือ syntax `...` (จุดสามจุด) ที่ใช้ **กระจาย** elements ใน array หรือ properties ใน object ออกเป็นค่าย่อย ๆ พูดง่าย ๆ: `...` นำหน้าชื่อ array หรือ object เพื่อ "แกะ" ข้างในออกมา — เหมือนเทของในกล่องออกมาวางทีละชิ้นบนโต๊ะ Spread ใช้ได้กับ: - **Array** / String / Iterable ใด ๆ — กระจาย element ออกเป็นค่าค่าหนึ่ง - **Object** — กระจาย property ออกเป็น key-value คู่ ๆ ตำแหน่งที่ใช้ Spread: - ใน **array literal** `[...arr]` - ใน **object literal** `{...obj}` - ใน **function call** `fn(...args)`
const nums = [1, 2, 3];
// Spread ใน array literal — กระจาย nums ออกแล้วเอามารวมใหม่
console.log([...nums]); // [1, 2, 3]
// Spread ใน function call — แต่ละ element กลายเป็น argument แยก
console.log(...nums); // 1 2 3 (console.log รับ 3 arguments แยกกัน)
console.log(1, 2, 3); // 1 2 3 (เหมือนเขียนมือแบบนี้)สังเกตว่า `console.log(...nums)` กับ `console.log(1, 2, 3)` ให้ผลลัพธ์เหมือนกัน — Spread "กระจาย" array ออกเป็น 3 arguments ให้ `console.log` โดยอัตโนมัติ Spread กับ Rest ใช้ syntax `...` เหมือนกัน แต่หน้าที่ตรงข้าม: - **Spread** = แกะ/กระจายของออก (ใช้ทางขวาของ `=`, ใน `{}`, `[]`, หรือ `()`) - **Rest** = รวบของที่เหลือเข้าไป (ใช้ทางซ้ายของ `=`, ใน parameter หรอ destructuring) บทนี้เรียน Spread ก่อน — Rest อยู่อีกบทถัดไป
Spread กับ Array — สร้าง shallow copy
ใช้ `[...arr]` เพื่อสร้าง **array ใหม่** ที่มี element เหมือนกันทุกประการ — ถ้า array ต้นฉบับเปลี่ยน array ใหม่จะไม่เปลี่ยนตาม นี่คือ **shallow copy**: copy แค่ชั้นแรก ถ้า element เป็น object จะยัง share reference กัน (รายละเอียดในหัวข้อ Shallow Copy)
const original = ["🍎", "🍌", "🍊"];
// สร้าง copy ด้วย spread
const copy = [...original];
// แก้ไข copy — original ไม่เปลี่ยน
copy.push("🍇");
copy[0] = "🍓";
console.log(original); // ["🍎", "🍌", "🍊"] — ไม่เปลี่ยน ✓
console.log(copy); // ["🍓", "🍌", "🍊", "🍇"]**ก่อนมี Spread** เราต้องใช้ `slice()` หรือ loop เพื่อ copy array: ```js const copy = original.slice(); // แบบเก่า const copy = [...original]; // Spread — สั้นและอ่านง่ายกว่า ``` Spread เป็นวิธี copy array ที่สั้นและชัดเจนที่สุด — เป็นที่นิยมในโค้ด JavaScript สมัยใหม่
Spread กับ Array — รวม array หลายตัว
ใช้ `[...a, ...b]` เพื่อรวม element จากหลาย array เข้าด้วยกันเป็น array ใหม่ — ยืดหยุ่นกว่า `concat()` เพราะสามารถแทรกค่าเพิ่มระหว่าง spread ได้
const frontend = ["React", "Vue", "Svelte"];
const backend = ["Node", "Python", "Go"];
// รวม 2 array
const allTech = [...frontend, ...backend];
console.log(allTech);
// ["React", "Vue", "Svelte", "Node", "Python", "Go"]
// แทรกค่าเพิ่มระหว่าง spread
const withExtra = ["HTML", ...frontend, "CSS", ...backend];
console.log(withExtra);
// ["HTML", "React", "Vue", "Svelte", "CSS", "Node", "Python", "Go"]
// แบบเก่าใช้ concat — ปรับตำแหน่งยาก
const oldWay = frontend.concat(backend);**ข้อดีของ Spread เหนือ `concat()`**: - เพิ่มค่าเดี่ยว ๆ คั่นกลางได้ (`[0, ...arr, 9]`) - รวมกี่ array ก็ได้ในบรรทัดเดียว - อ่านลำดับ element ได้ชัดเจนจากซ้ายไปขวา - ใช้ syntax เดียวกันกับ object spread — จำง่าย
Spread กับ Object — copy, merge, override
Spread ใช้กับ object ในรูปแบบ `{...obj}` เพื่อ: - **Copy**: สร้าง object ใหม่ที่มี property เหมือนเดิม - **Merge**: รวม property จากหลาย object เข้าด้วยกัน - **Override**: เปลี่ยนค่า property บางตัวโดยไม่แตะ object ต้นฉบับ **กฎสำคัญ**: ถ้า key ซ้ำ — ตัวขวาสุดชนะ (override)
const defaults = {
theme: "light",
language: "en",
fontSize: 14,
};
const userPrefs = {
theme: "dark",
language: "th",
};
// Merge (รวม) — key ซ้ำ → ตัวขวาสุดชนะ
const config = { ...defaults, ...userPrefs };
console.log(config);
// { theme: "dark", language: "th", fontSize: 14 }
// Override (เปลี่ยนค่า) บาง property
const updated = { ...defaults, theme: "dark" };
console.log(updated);
// { theme: "dark", language: "en", fontSize: 14 }
// original ไม่เปลี่ยน — spread สร้าง object ใหม่เสมอ
console.log(defaults.theme); // "light" ✓Spread object เป็น pattern ที่ใช้บ่อยมากใน React และ JavaScript สมัยใหม่สำหรับ: - อัปเดต state โดยไม่ mutate ของเก่า (immutable update) - รวมค่า default กับค่าที่ผู้ใช้กำหนด - สร้าง object ใหม่จากของเก่าโดยเปลี่ยนแค่บาง field **ข้อควรระวัง**: Spread object จะ copy เฉพาะ **own enumerable properties** — ไม่ copy properties ใน prototype chain
Spread เป็น Shallow Copy — ข้อควรระวังสำคัญ
**Spread ทำ shallow copy เท่านั้น** — property หรือ element ที่เป็น object จะถูก copy แค่ reference ไม่ได้สร้าง object ใหม่ให้ แปลว่าถ้า array/object ต้นฉบับมี nested object อยู่ข้างใน — nested ชั้นในจะยัง **share reference** กับต้นฉบับ การแก้ไข nested object ใน copy จะกระทบต้นฉบับด้วย!
const original = {
name: "สมชาย",
address: { city: "กรุงเทพ", zip: "10100" }, // nested object
};
const copy = { ...original };
// property ระดับแรก (primitive) — แยกกัน
copy.name = "สมหญิง";
console.log(original.name); // "สมชาย" — ไม่เปลี่ยน ✓
// nested object — ยัง share reference อยู่!
copy.address.city = "เชียงใหม่";
console.log(original.address.city); // "เชียงใหม่" — เปลี่ยนตาม! ⚠
// ดู reference — เป็น object เดียวกัน
console.log(copy.address === original.address); // true**วิธีแก้เมื่อต้องการ copy ลึก (deep copy)**: - `structuredClone(obj)` — วิธีมาตรฐานที่ built-in ใน JavaScript - `JSON.parse(JSON.stringify(obj))` — ใช้ได้เฉพาะข้อมูลที่ไม่มี function/undefined แต่ในทางปฏิบัติส่วนใหญ่ shallow copy ก็เพียงพอ — เพราะเรามักเปลี่ยนแค่ property ระดับแรก (เช่น `name`, `age`, `theme`)
Spread ใน Function Call
Spread ใช้ใน function call เพื่อส่ง element ของ array เป็น argument แยกกัน — มีประโยชน์กับ function ที่รับหลาย argument (เช่น `Math.max`, `Math.min`, `console.log`)
// Math.max รับหลาย argument — Spread ทำให้ส่ง array ได้
const scores = [78, 92, 85, 66, 95];
console.log(Math.max(...scores)); // 95
console.log(Math.min(...scores)); // 66
// เทียบกับแบบเก่า — ต้องใช้ apply
console.log(Math.max.apply(null, scores)); // 95 (แบบเก่า — ซับซ้อนกว่า)
// ใช้กับ function ที่เขียนเองก็ได้
function sum(a, b, c) {
return a + b + c;
}
const nums = [10, 20, 30];
console.log(sum(...nums)); // 60
// console.log รับกี่ argument ก็ได้
const items = ["เริ่ม", "กลาง", "จบ"];
console.log(...items); // เริ่ม กลาง จบ- Spread ใน function call มีประโยชน์มากกับ `Math.max/min` — แทนที่ `apply()` แบบเก่า
- ใช้กับ function ที่เขียนเองก็ได้ ตราบใดที่จำนวน argument พอดีกับ function
- Spread ใช้กับ iterable ใด ๆ ก็ได้ (array, string, Set, Map)
กฎสำคัญและข้อควรระวัง
| Syntax | ความหมาย | ข้อควรระวัง |
|---|---|---|
| `[...arr]` | สร้าง shallow copy ของ array arr | element ที่เป็น object/nested array ยัง share reference |
| `[...a, ...b]` | รวม element จาก a และ b เป็น array ใหม่ | ลำดับ element เป็นไปตามลำดับ spread จากซ้ายไปขวา |
| `[0, ...arr, 9]` | แทรกค่าคั่นระหว่าง spread | ค่าที่แทรกเป็น primitive ธรรมดา — ไม่ต้องเป็น array |
| `{...obj}` | สร้าง shallow copy ของ object obj | copy เฉพาะ own enumerable properties — ไม่รวม prototype |
| `{...a, ...b}` | รวม properties จาก a และ b — key ซ้ำตัวขวาชนะ | ลำดับสำคัญ — `{...a, ...b}` ไม่เท่ากับ `{...b, ...a}` ถ้ามี key ซ้ำ |
| `{...obj, key: val}` | Override property key ด้วยค่า val | obj ไม่เปลี่ยน — spread สร้าง object ใหม่เสมอ |
| `fn(...arr)` | ส่ง element ของ arr เป็น argument แยกให้ fn | จำนวน element ต้องพอดีกับ parameter ที่ fn รับ (หรือ fn ใช้ rest) |
Spread operator เป็นหนึ่งใน syntax ที่ใช้บ่อยที่สุดใน JavaScript สมัยใหม่ — ลองฝึกกับตัวอย่างและ lab ด้านล่างเพื่อให้ใช้ได้คล่องก่อนนำไปใช้จริง