เขียน JavaScript ปี 2026 อาจไม่ต้องลง lodash แล้ว มีทั้ง Object.groupBy, Set methods และ Iterator Helpers ให้ใช้ในภาษา
หลายอย่างใน JavaScript ที่เมื่อก่อนต้องพึ่ง lodash หรือเขียน util เอง ตอนนี้มีมาในภาษาแล้ว รวมฟีเจอร์ที่ใช้แทนได้จริงในวันนี้ พร้อมแยกว่าอันไหนใช้ได้สบายแล้ว อันไหนต้องเช็ก runtime ก่อน

เวลาเขียน JavaScript ภาษาโปรแกรมหลักของงานฝั่งเว็บ เรามักมีรายการข้อมูลยาวๆ (array) อยู่ในมือ แล้วอยากหยิบแค่สมาชิกตัวสุดท้าย หรืออยากจัดกลุ่มมันตามค่าบางอย่าง เช่น แยกรายการสั่งซื้อออกตามชื่อลูกค้า งานพื้นๆ แบบนี้ในอดีตกลับยุ่งกว่าที่ควร จะเอาตัวสุดท้ายก็ต้องพิมพ์ arr[arr.length - 1] จะจัดกลุ่มก็ต้องลง lodash (ไลบรารีรวมฟังก์ชันช่วยงานที่สาย JavaScript ลงกันจนติดมือ) มาทั้งก้อน แค่เพื่อใช้ฟังก์ชัน groupBy ตัวเดียว
นิสัยแบบนี้เราติดกันมาหลายปี เพราะเมื่อก่อนภาษาไม่มีฟีเจอร์เหล่านี้ให้ แต่ปี 2026 JavaScript มีมาให้แล้วหลายอย่าง อยากได้ตัวสุดท้ายก็เขียน arr.at(-1) ตรงๆ อยากจัดกลุ่มก็เรียก Object.groupBy() ได้เลย ไม่ต้องลงไลบรารีเพิ่ม ไม่ต้องก๊อปโค้ดช่วยงานเดิมๆ มาวางซ้ำอีก
ของพวกนี้ไม่ได้โผล่มาพร้อมกันในวันเดียว แต่ทยอยผ่านขั้นสุดท้ายของ TC39 (คณะทำงานที่ดูแลมาตรฐานภาษา JavaScript) ในช่วงไม่กี่ปี ขั้นนี้เรียกว่า Stage 4 หรือ finished หมายความว่าออกแบบฟีเจอร์เสร็จและบรรจุลงในสเปกอย่างเป็นทางการแล้ว แต่ก่อนหยิบไปใช้ต้องแยกให้ออกว่า การเข้าสเปกไม่ได้แปลว่าวันนี้จะรันได้ในทุกเบราว์เซอร์ บางตัวเก่าและนิ่งจนใช้ได้สบายแล้ว บางตัวยังใหม่และรองรับไม่ครบทุกที่ จุดนี้ต้องเช็กให้ดีก่อนนำไปใช้จริง
งานที่เคยต้องพึ่ง lodash ตอนนี้เขียนได้สั้นลง

เริ่มจากงานที่เจอทุกวันอย่างการจัดกลุ่มข้อมูล เมื่อก่อนถ้าอยากแยกรายการออเดอร์ตามสถานะ ต้องเขียน reduce (วิธีวนรวมข้อมูลทีละตัว) ยาวๆ เพื่อรวบข้อมูลเป็นกลุ่มเอง ผลลัพธ์จะเป็น object ซึ่งเก็บชื่อกับค่าที่จับคู่กันไว้
// เดิม: reduce ยาวๆ เพื่อจัดกลุ่มออเดอร์ตามสถานะ
const byStatus = orders.reduce((acc, o) => {
(acc[o.status] ??= []).push(o);
return acc;
}, {});
ตอนนี้เรียก Object.groupBy() ทีเดียวจบ บอกแค่ว่าอยากจัดกลุ่มด้วยค่าอะไร ที่เหลือภาษาจัดให้
// ตอนนี้: บรรทัดเดียว
const byStatus = Object.groupBy(orders, (o) => o.status);
การหยิบสมาชิกจาก array ก็เหมือนกัน แทนที่จะพิมพ์ arr[arr.length - 1] เพื่อเอาตัวสุดท้าย ก็ใช้ arr.at(-1) ซึ่งอ่านง่ายกว่า และใส่เลขติดลบเพื่อไล่นับจากท้ายได้เลย เช่น arr.at(-2) คือตัวที่สองนับจากท้าย
อีกกลุ่มที่เคยต้องเขียนเองคือการเทียบชุดข้อมูลสองชุด ตอนนี้ Set (โครงสร้างเก็บข้อมูลที่ไม่มีค่าซ้ำ) มีเมท็อดสำหรับงานนี้ ทั้งหาสมาชิกที่อยู่ในทั้งสองชุด รวมสมาชิกจากสองชุด และหาสมาชิกที่ต่างกัน
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
a.intersection(b); // Set {2, 3} ตัวที่มีอยู่ทั้งสองชุด
a.union(b); // Set {1, 2, 3, 4} รวมสองชุดเข้าด้วยกัน
a.difference(b); // Set {1} ตัวที่มีใน a แต่ไม่มีใน b
เดิมงานพวกนี้ต้องวนลูปเทียบเองหรือใช้ฟังก์ชันจาก lodash ตอนนี้มีเป็นเมท็อดของ Set ให้เรียกใช้ตรงๆ
เลิกเผลอแก้ของเดิม บั๊กจากการแก้ข้อมูลโดยไม่รู้ตัวก็หาย
บั๊กที่ตามหายากมักไม่ได้แสดงอาการชัดๆ แต่เกิดจากการเผลอแก้ข้อมูลต้นฉบับโดยไม่ตั้งใจ ตัวอย่างคลาสสิกคือ .sort() ชื่อของมันชวนให้คิดว่าเราจะได้ array ที่เรียงแล้วกลับมา แต่จริงๆ มันเรียงทับ array เดิมด้วย
const scores = [3, 1, 2];
scores.sort(); // ได้ [1, 2, 3] แต่ scores ตัวเดิมก็เปลี่ยนตามไปแล้ว
ถ้ามีโค้ดส่วนอื่นถือ scores ชุดเดิมอยู่ ก็จะเจอว่าข้อมูลชุดนั้นเรียงใหม่ไปเองโดยไม่รู้ตัว เมท็อดชุดใหม่แก้ปัญหานี้ด้วยการสร้างสำเนามาแก้แทน จึงไม่แตะข้อมูลเดิม
const scores = [3, 1, 2];
const sorted = scores.toSorted(); // ได้ชุดใหม่ [1, 2, 3] ส่วน scores เดิมอยู่เหมือนเดิม
ยังมี toReversed() สำหรับกลับลำดับ และ with(i, value) สำหรับเปลี่ยนค่าตำแหน่งเดียวโดยไม่แตะ array เดิม ทั้งหมดนี้ช่วยลดบั๊กจากการเผลอแก้ค่าต้นฉบับ (mutate) ได้มาก
เรื่องการก๊อปข้อมูลก็คล้ายกัน เมื่อก่อนถ้าอยากก๊อป object ที่ซ้อนหลายชั้นโดยไม่ให้ผูกกับต้นฉบับ ทริกที่ใช้กันคือ JSON.parse(JSON.stringify(obj)) ซึ่งใช้ไม่ได้กับข้อมูลบางชนิด ตอนนี้ทั้งเบราว์เซอร์และ Node (ตัวรัน JavaScript นอกเบราว์เซอร์) มี structuredClone() มาให้ ก๊อปข้อมูลที่ซ้อนกันทุกชั้นได้ในบรรทัดเดียว
Iterator Helpers จัดการข้อมูลชุดใหญ่โดยใช้แรงประมวลผลและหน่วยความจำน้อยลง
ฟีเจอร์ที่เปลี่ยนวิธีคิดได้เยอะคือ Iterator Helpers เวลาเราต่อ .filter().map() กันบน array แต่ละขั้นจะสร้าง array ชุดใหม่ขึ้นมาระหว่างทาง ถ้าข้อมูลยาวมากก็เสียทั้งแรงประมวลผลและหน่วยความจำโดยเปล่าประโยชน์ เพราะ array ชั่วคราวเหล่านี้ใช้เพียงแป๊บเดียว
Iterator Helpers ให้เราต่อ .filter() .map() .take() บน iterator (ตัวไล่ข้อมูลทีละตัว) ได้ตรงๆ และทำงานแบบ lazy คือดึงข้อมูลมาเท่าที่ต้องใช้จริง ไม่ไล่ทั้งชุด ลองดูตัวอย่าง สมมติมีฟังก์ชัน naturals() ที่เราเขียนขึ้นเองให้ไล่เลข 1, 2, 3 ต่อไปเรื่อยๆ ไม่รู้จบ
// naturals() = ลำดับเลข 1, 2, 3, ... ที่ยาวไม่รู้จบ
const result = naturals()
.filter((n) => n % 2 === 0) // เอาเฉพาะเลขคู่
.map((n) => n * n) // ยกกำลังสอง
.take(5); // เอาแค่ 5 ตัวแรกพอ
[...result]; // [4, 16, 36, 64, 100]
จุดสำคัญคือลำดับนี้ยาวไม่รู้จบ แต่โค้ดไม่ค้าง เพราะ .take(5) บอกให้หยุดเมื่อได้ครบ 5 ตัว มันเลยไล่คำนวณแค่เท่าที่จำเป็นแล้วหยุด ไม่พยายามสร้างลิสต์ที่ยาวไม่รู้จบขึ้นมาทั้งหมดก่อน
งานแบบ async คืองานที่ผลลัพธ์ไม่ได้มาทันที เช่น การรอโหลดข้อมูลจากเซิร์ฟเวอร์ งานแบบนี้มักใช้ Promise แทนผลลัพธ์ที่ยังไม่รู้ทันทีว่าจะสำเร็จหรือล้มเหลว JavaScript มีตัวช่วยสำหรับงานลักษณะนี้เพิ่มมาอีกหลายตัวให้เลือกใช้ตามสถานการณ์
Array.fromAsync()สำหรับตอนที่ต้องดึงข้อมูลจากแหล่ง async เช่น API (ช่องทางที่โปรแกรมเรียกขอข้อมูลจากบริการอื่น) ที่ทยอยส่งมาทีละหน้า แล้วอยากรวบรวมเป็น array ชุดเดียวPromise.withResolvers()สำหรับตอนที่อยากได้ตัวสั่งว่างานสำเร็จและตัวสั่งว่างานล้มเหลว แยกออกมาเรียกจากที่อื่นในโค้ดได้ ไม่ต้องผูกไว้ข้างในตอนสร้าง PromisePromise.try()สำหรับเรียกฟังก์ชันที่อาจพังได้ ไม่ว่าจะพังทันทีตอนเรียกหรือพังระหว่างรองาน async ทั้งสองกรณีจะเข้า.catch()จุดเดียวกันเพื่อดักจับ error ไม่ต้องเขียนโค้ดแยกสองทาง
finished ไม่เท่ากับใช้ได้ทุกที่

ของทั้งหมดข้างบนไม่ได้เสร็จพร้อมกัน แต่ทยอยเสร็จคนละปี ตั้งแต่ราวปี 2024 ถึงต้นปี 2026 แม้จะ finished แล้ว แต่ละตัวก็อยู่ในมาตรฐาน ECMAScript คนละรุ่น มาตรฐานนี้เป็นสเปกกลางของ JavaScript ที่ออกรุ่นใหม่ทุกปี ฟีเจอร์ในรุ่นเก่ากว่าอย่าง .at() หรือชุด toSorted() เบราว์เซอร์ปัจจุบันรองรับกันทั่วแล้ว ส่วนฟีเจอร์ในรุ่นใหม่กว่านั้นยังทยอยรองรับไม่ครบ
สองตัวที่ต้องระวังเป็นพิเศษคือ Temporal กับ using ตัวแรก Temporal เป็น API ใหม่สำหรับจัดการวันเวลา เขียนมาแทน Date ตัวเก่าที่ใช้ยากและมีกับดักเยอะ จุดเด่นคือค่าเวลาที่สร้างแล้วเปลี่ยนไม่ได้ (immutable) จึงส่งต่อได้โดยไม่เผลอแก้ค่าต้นฉบับ อีกทั้งยังรองรับ timezone หรือเขตเวลาของแต่ละพื้นที่ในตัว แต่ Temporal มีกำหนดอยู่ในมาตรฐานรุ่นปี 2027 ซึ่งช้ากว่าตัวอื่น จึงยังทยอยรองรับไม่ครบทุกที่ ฝั่ง Node เท่าที่ประกาศไว้เปิด Temporal เป็นค่าเริ่มต้นตั้งแต่ Node 26 ส่วนเบราว์เซอร์อย่าง Safari ยังตามหลัง
อีกตัวคือ using และ await using (เรียกรวมว่า explicit resource management) เวลาเราเปิดทรัพยากรที่ต้องปิดภายหลัง เช่น ไฟล์หรือการเชื่อมต่อฐานข้อมูล ปกติต้องเขียนคำสั่งปิดเองใน try/finally ซึ่งเป็นโครงสร้างโค้ดที่รับประกันว่าคำสั่งใน finally จะทำงานเสมอ using ช่วยย่อขั้นตอนนี้ เมื่อตัวแปรพ้นขอบเขตการใช้งาน ระบบจะปิดทรัพยากรที่เปิดค้างไว้ให้อัตโนมัติ ฟีเจอร์นี้มีกำหนดอยู่ในมาตรฐานรุ่นปี 2027 เช่นกัน และยังใหม่จนต้องเช็กก่อนว่ารันไทม์ (ตัวที่รันโค้ดของเรา เช่น เบราว์เซอร์หรือ Node) รองรับหรือยัง
กฎง่ายๆ ก่อนเอาโค้ดขึ้นใช้งานจริง คือเปิดดู web.dev Baseline หรือ MDN ก่อนเสมอว่าฟีเจอร์ที่จะใช้รองรับครบในกลุ่มเบราว์เซอร์เป้าหมายหรือยัง ปีที่เข้าสเปกเป็นแค่ตัวช่วยกะคร่าวๆ ว่าฟีเจอร์นิ่งแค่ไหน ไม่ใช่การันตีว่ารันได้ทุกที่ จะใช้ของใหม่พวกนี้ได้ก็ต่อเมื่อใช้เบราว์เซอร์รุ่นใหม่และ Node รุ่นปัจจุบัน เครื่องมือฝั่ง JavaScript ก็เปลี่ยนเร็วมาก ถึงขั้นที่ Node.js, Astro และ Vite เคยออกเวอร์ชันใหญ่พร้อมกันในสัปดาห์เดียว การอัปเดตรันไทม์ให้ทันจึงเป็นส่วนหนึ่งของงาน
เช็กลิสต์: โค้ดเดิมตรงไหนเปลี่ยนมาใช้ฟีเจอร์ในตัวได้บ้าง
เอาไปใช้ได้เลย ลองไล่โค้ดหรือฟังก์ชันช่วยงาน (util) เดิมในโปรเจกต์ แล้วเทียบกับสองกลุ่มนี้
กลุ่มแรกเป็นฟีเจอร์ที่นิ่งพอแล้ว เปลี่ยนได้เลย (แต่ยังควรยืนยันกับ Baseline สักครั้ง)
- ยังพิมพ์
arr[arr.length - 1]อยู่ →arr.at(-1) - ยังเขียน
reduceยาวๆ แค่เพื่อจัดกลุ่ม →Object.groupBy() - ยังวนลูปหาตัวซ้ำหรือรวมชุดข้อมูล →
.intersection().union().difference()ของ Set - ยังใช้
.sort().reverse()แล้วเผลอแก้ของเดิม →toSorted()toReversed()with() - ยังใช้
JSON.parse(JSON.stringify(obj))เพื่อก๊อป object ที่ซ้อนกันทุกชั้น →structuredClone() - ยังต่อ
.filter().map()บนข้อมูลชุดใหญ่ → Iterator Helpers แบบ lazy
กลุ่มที่สองยังใหม่ เช็ก runtime ก่อนค่อยใช้
- อยากเลิกปวดหัวกับ
Date→ Temporal (Safari ยังตามหลัง เช็กก่อน) - อยากให้โค้ดปิดทรัพยากรที่เปิดไว้ให้เอง ไม่ต้องเขียน
try/finally→using/await using - อยากบวกเลขหลายตัวไม่ให้ผลรวมเพี้ยนเพราะการปัดเศษของคอมพิวเตอร์ →
Math.sumPrecise() - อยากเช็กว่าเป็น error จริงไหม แม้ตัวมันจะมาจากคนละหน้าต่างหรือคนละส่วนของโปรแกรม →
Error.isError() - ยังเขียนเช็กก่อนว่า Map (ที่เก็บข้อมูลแบบจับคู่คีย์กับค่า) มีคีย์นี้ไหม ถ้าไม่มีค่อยใส่ →
Map.getOrInsert()(ตัวที่เพิ่งเสร็จล่าสุดต้นปี 2026) - ยังต้องพึ่ง bundler (เครื่องมือรวมไฟล์ตอน build) แค่เพื่อโหลดไฟล์ JSON →
import data from "./x.json" with { type: "json" }
ของพวกนี้ไม่ได้มีไว้ให้เราไล่จำจนครบทุกตัว แต่การเริ่มใช้จริงทำได้ง่ายกว่านั้นมาก ลองเปิดไฟล์ util เดิมในโปรเจกต์ตัวเองสักไฟล์ หาฟังก์ชันช่วยงานสักตัวที่ JavaScript มีฟีเจอร์แทนได้แล้ว จากนั้นลบฟังก์ชันเดิมทิ้ง พอมีโค้ดน้อยลงหนึ่งฟังก์ชัน จุดที่เกิดบั๊กจากการดูแลเองก็หายไปหนึ่งจุด แค่นี้ก็เริ่มได้แล้ว
ที่มา: ลิสต์ TC39 finished proposals บน GitHub
ชอบเรื่องแนวนี้ มีอีบุ๊คฟรีให้อ่านต่อ
NotebookLM ฉบับเข้าใจง่าย โยนเอกสารให้ AI อ่าน แล้วได้สรุป พอดแคสต์ และคลังความรู้ส่วนตัว
กดสมัครแล้วเราจะส่งเทคนิค AI และของแจกใหม่ๆ ให้ทางอีเมล เลิกรับได้ตลอด



