Category: reference
JavaScript Modern APIs (2023–2025)
APIs ใหม่ที่ใช้ได้ใน modern browsers และ Node.js: Object.groupBy, Array methods ใหม่, structuredClone, Promise.withResolvers, และอื่นๆ
สารบัญ
Array Methods ใหม่ (ES2023+)
toSorted / toReversed / toSpliced — ไม่ mutate
const nums = [3, 1, 4, 1, 5];
// ❌ เดิม: mutate array ต้นฉบับ
nums.sort(); // nums เปลี่ยนแล้ว!
nums.reverse(); // nums เปลี่ยนแล้ว!
// ✓ ใหม่: คืน array ใหม่ ต้นฉบับไม่เปลี่ยน
const sorted = nums.toSorted(); // [1, 1, 3, 4, 5]
const reversed = nums.toReversed(); // [5, 1, 4, 1, 3]
const spliced = nums.toSpliced(2, 1); // [3, 1, 1, 5] (ลบ index 2)
const withNew = nums.with(0, 99); // [99, 1, 4, 1, 5] (แทน index 0)
nums; // [3, 1, 4, 1, 5] ← ไม่เปลี่ยน
// มีประโยชน์มากกับ React state:
setItems(items.toSorted((a, b) => a.name.localeCompare(b.name)));
Array.at() — Negative Indexing
const arr = [1, 2, 3, 4, 5];
arr.at(0) // 1
arr.at(-1) // 5 (element สุดท้าย)
arr.at(-2) // 4
// ✓ เทียบกับ arr[arr.length - 1] ที่ verbose กว่า
const last = arr.at(-1);
// ใช้กับ string ด้วย
'hello'.at(-1) // 'o'
Object.groupBy / Map.groupBy (ES2024)
const people = [
{ name: 'Alice', dept: 'Engineering' },
{ name: 'Bob', dept: 'Design' },
{ name: 'Carol', dept: 'Engineering' },
{ name: 'Dave', dept: 'Design' },
];
// ✓ groupBy — คืน object ที่ key คือ group
const byDept = Object.groupBy(people, (p) => p.dept);
// {
// Engineering: [{ name: 'Alice', ... }, { name: 'Carol', ... }],
// Design: [{ name: 'Bob', ... }, { name: 'Dave', ... }]
// }
// Map.groupBy — เมื่อ key ไม่ใช่ string (object, symbol, etc.)
const byNameLength = Map.groupBy(people, (p) => p.name.length);
// Map { 5 => [...], 3 => [...], 4 => [...] }
// เทียบกับ reduce เดิม:
const byDeptOld = people.reduce((acc, p) => {
(acc[p.dept] ??= []).push(p);
return acc;
}, {});
structuredClone — Deep Clone ที่ถูกต้อง
// ❌ JSON round-trip: ไม่รองรับ Date, undefined, circular refs
const clone = JSON.parse(JSON.stringify(obj));
// ❌ spread/assign: shallow copy เท่านั้น
const clone = { ...obj }; // nested objects ยัง reference เดียวกัน
// ✓ structuredClone: deep clone ที่ถูกต้อง
const original = {
name: 'Alice',
created: new Date('2024-01-01'),
tags: ['ts', 'css'],
nested: { score: 100 },
};
const clone = structuredClone(original);
clone.tags.push('node');
clone.nested.score = 200;
original.tags; // ['ts', 'css'] — ไม่เปลี่ยน
original.nested.score; // 100 — ไม่เปลี่ยน
clone.created; // Date object (ไม่ใช่ string)
// รองรับ: Date, RegExp, Map, Set, ArrayBuffer, TypedArray, Error
// ไม่รองรับ: Function, Symbol, DOM nodes, class instances (เสีย methods)
Promise.withResolvers (ES2024)
// ❌ เดิม: verbose boilerplate
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// ใช้ resolve/reject ภายนอก...
// ✓ ใหม่: clean API
const { promise, resolve, reject } = Promise.withResolvers();
// ตัวอย่าง: wrap event เป็น Promise
function waitForEvent(element, eventName) {
const { promise, resolve, reject } = Promise.withResolvers();
const controller = new AbortController();
element.addEventListener(eventName, resolve, { once: true, signal: controller.signal });
setTimeout(() => {
controller.abort();
reject(new Error(`Timeout waiting for ${eventName}`));
}, 5000);
return promise;
}
await waitForEvent(button, 'click');
Nullish Coalescing Assignment (??=, &&=, ||=)
// ??= : assign ถ้า null หรือ undefined เท่านั้น
let user = null;
user ??= { name: 'Guest' }; // user = { name: 'Guest' }
let count = 0;
count ??= 10; // count ยังเป็น 0 (ไม่ใช่ null/undefined)
// ||= : assign ถ้า falsy (0, '', false, null, undefined)
let title = '';
title ||= 'Untitled'; // title = 'Untitled'
// &&= : assign ถ้า truthy
let config = { debug: false };
config &&= { ...config, version: '1.0' }; // config = { debug: false, version: '1.0' }
// ใช้บ่อยมากสำหรับ cache:
const cache = {};
function getUser(id) {
cache[id] ??= fetchUser(id); // โหลดครั้งเดียว
return cache[id];
}
Error.cause (ES2022)
// ✓ ส่ง original error เป็น cause
try {
await db.connect(url);
} catch (err) {
throw new Error('Database connection failed', { cause: err });
}
// อ่าน cause:
try {
await connectDB();
} catch (err) {
console.error(err.message); // 'Database connection failed'
console.error(err.cause); // original db error
}
Array.fromAsync (ES2024)
// สร้าง array จาก async iterable
async function* generateItems() {
for (let i = 0; i < 5; i++) {
await delay(100);
yield { id: i, value: i * 2 };
}
}
const items = await Array.fromAsync(generateItems());
// [{ id: 0, value: 0 }, { id: 1, value: 2 }, ...]
// เทียบกับ
const items = [];
for await (const item of generateItems()) {
items.push(item);
}
using / await using — Explicit Resource Management (ES2024)
// using: cleanup อัตโนมัติเมื่อออก scope (sync)
{
using file = openFile('data.txt');
// ใช้ file...
} // ← file.close() ถูกเรียกอัตโนมัติ
// await using: async cleanup
async function processData() {
await using db = await Database.connect(url);
await using tx = await db.beginTransaction();
// ใช้ tx...
} // ← tx.rollback() หรือ tx.commit() และ db.disconnect() ถูกเรียกอัตโนมัติ
// implement Disposable interface:
class FileHandle {
[Symbol.dispose]() {
this.close(); // ถูกเรียกเมื่อ using block จบ
}
}
Browser Support Summary
| Feature | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
Array.at() | 92 | 90 | 15.4 | 16.6 |
toSorted/Reversed | 110 | 115 | 16 | 20 |
Object.groupBy | 117 | 121 | 17.4 | 21 |
structuredClone | 98 | 94 | 15.4 | 17 |
Promise.withResolvers | 119 | 121 | 17.4 | 22 |
??= / &&= / ||= | 85 | 79 | 14 | 15 |
using keyword | 134 | — | — | 22 |