jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

What's New in ES2025 & ES2026 — The Complete Guide with Examples

A bilingual, example-driven tour of ECMAScript 2025 (Iterator Helpers, Set methods, Promise.try, RegExp.escape, import attributes, Float16Array) and ES2026 (explicit resource management with using, Array.fromAsync, Error.isError).

Chu kỳ phát hành hàng năm

Từ ES2016, TC39 phát hành một phiên bản ECMAScript mới mỗi tháng Sáu. Một proposal trở thành một phần của ngôn ngữ khi đạt Stage 4.

  • ES2025 (phiên bản thứ 16) được chốt vào 25/06/2025.
  • ES2026 đang trên đà được duyệt vào khoảng tháng 6/2026; các tính năng dưới đây đã đạt Stage 4 và có mặt trong các engine hiện đại.

Đây là chuyến đi thực hành — mỗi tính năng đều có ví dụ chạy được.

Ghi chú tên gọi: ""ESxxxx” chỉ năm spec được xuất bản, không phải khi engine triển khai. Nhiều tính năng đến với trình duyệt trước khi phiên bản được duyệt chính thức.


Phần 1 — ECMAScript 2025

Iterator Helpers — Phương thức kiểu Array nhưng lười

Cải thiện trải nghiệm lớn nhất trong ES2025. Iterator giờ có .map(), .filter(), .take(),… — nhưng khác array, chúng lười (lazy).

function* naturals() {
  let n = 1;
  while (true) yield n++; // infinite!
}

const result = naturals()
  .map((n) => n ** 2)   // 1, 4, 9, 16, ...
  .filter((n) => n % 2) // keep odds
  .take(5)              // stop after 5
  .toArray();           // only NOW does work happen

console.log(result); // [1, 9, 25, 49, 81]

Tại sao quan trọng: với array, [...infinite].map().filter() sẽ treo mãi vì mỗi bước dựng một array trung gian đầy đủ. Iterator helper xử lý từng phần tử một, theo yêu cầu, nên chạy được trên chuỗi vô hạn hay khổng lồ và tránh cấp phát trung gian.

Các helper có sẵn: map, filter, flatMap, take, drop, reduce, toArray, forEach, some, every, find, cùng phương thức tĩnh Iterator.from().

// Iterator.from() wraps any iterable so you can chain helpers on it
const firstThree = Iterator.from(['a', 'b', 'c', 'd']).take(3).toArray();
console.log(firstThree); // ['a', 'b', 'c']

Set Methods — Đại số tập hợp thật sự

Set cuối cùng đã có các phép toán mà ta luôn phải tự cài lại bằng tay.

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

a.union(b);              // Set {1, 2, 3, 4, 5, 6}
a.intersection(b);       // Set {3, 4}
a.difference(b);         // Set {1, 2}        (in a, not b)
a.symmetricDifference(b);// Set {1, 2, 5, 6}  (in exactly one)

a.isSubsetOf(b);         // false
a.isSupersetOf(new Set([1, 2])); // true
a.isDisjointFrom(new Set([7, 8])); // true (no overlap)

Chúng trả về Set mới (không biến đổi) và nhận bất kỳ đối số dạng set nào. Không còn phải viết new Set([...a].filter(x => b.has(x))).


Promise.try — Bắt đầu chuỗi một cách an toàn

Promise.try(fn) chạy fnluôn trả về Promise — dù fn trả giá trị, trả promise, hay throw đồng bộ.

// Problem: a sync throw escapes the promise chain
function getConfig(raw) {
  if (!raw) throw new Error('missing config'); // sync throw!
  return JSON.parse(raw);
}

// ❌ The throw happens BEFORE any .catch() can catch it
getConfig(input).then(use).catch(handle); // may crash synchronously

// ✅ Promise.try funnels sync + async errors into the same chain
Promise.try(() => getConfig(input))
  .then(use)
  .catch(handle); // catches the sync throw too

Đây là phiên bản chuẩn hóa của một mẫu mà các thư viện như Bluebird đã phổ biến.


RegExp.escape — Pattern động an toàn

Dựng regex từ input người dùng luôn rủi ro — ký tự đặc biệt như . hay ( làm đổi ý nghĩa. RegExp.escape() vô hiệu hóa chúng.

const userInput = 'a.b(c)';

// ❌ '.' matches any char, '()' is a group → wrong + injection risk
new RegExp(userInput);

// ✅ escape first
const safe = RegExp.escape(userInput); // 'a\\.b\\(c\\)'
const re = new RegExp(safe);
console.log(re.test('a.b(c)')); // true — matches literally

Regex: Cờ inline + nhóm tên trùng

Cờ inline cho phép bật/tắt flag cho một phần của pattern:

// (?i:...) → case-insensitive ONLY inside the group
const re = /hello-(?i:world)/;
re.test('hello-WORLD'); // true  ('world' part is case-insensitive)
re.test('HELLO-world'); // false ('hello' part is still case-sensitive)

Nhóm tên trùng giờ được phép giữa các nhánh alternation:

// Previously a SyntaxError; now valid because the names are in
// mutually-exclusive branches of the alternation.
const date = /(?<year>\d{4})-\d{2}|\d{2}-(?<year>\d{4})/;
'2026-05'.match(date).groups.year; // '2026'
'05-2026'.match(date).groups.year; // '2026'

Import Attributes & JSON Modules

Giờ bạn có thể import JSON một cách gốc, khai báo type bằng từ khóa with:

// Static import
import config from './config.json' with { type: 'json' };

// Dynamic import
const data = await import('./data.json', { with: { type: 'json' } });

console.log(config.version);

Attribute with { type: 'json' } báo cho runtime cách parse và validate module. Đây cũng là tính năng bảo mật — runtime sẽ không thực thi một URL .json như JavaScript. JSON là type đầu tiên được hỗ trợ; sẽ có thêm sau.


Float16Array — Số thực nửa độ chính xác

Một typed array mới cho số thực 16-bit, hữu ích cho GPU/ML, đồ họa, và dữ liệu nhạy băng thông.

const floats = new Float16Array([1.5, 2.25, 3.125]);
console.log(floats[0]); // 1.5 (stored in 2 bytes instead of 4 or 8)

// Companion APIs:
const view = new DataView(new ArrayBuffer(2));
view.setFloat16(0, 3.14);
view.getFloat16(0);   // ~3.14 (rounded to half precision)
Math.f16round(3.14159); // nearest value representable as a float16

Nửa độ chính xác đánh đổi độ chính xác lấy một nửa bộ nhớ so với Float32Array — đánh đổi phổ biến trong tensor học máy.


Phần 2 — ECMAScript 2026

Những thứ này đã đạt Stage 4 và có trong các engine hiện tại; dự kiến vào phiên bản ES2026 (≈ tháng 6/2026).

Quản lý tài nguyên tường minh — using & await using

Tính năng đinh của ES2026. Khai báo using mới tự động dọn dẹp tài nguyên khi ra khỏi scope — như destructor, như with của Python, hay using của C#.

Một tài nguyên disposable định nghĩa phương thức [Symbol.dispose]():

function openFile(path) {
  const handle = acquireHandle(path);
  return {
    handle,
    read() { return readFrom(this.handle); },
    [Symbol.dispose]() {
      releaseHandle(handle); // runs automatically at scope exit
    },
  };
}

function process() {
  using file = openFile('/tmp/data');
  const text = file.read();
  return text.toUpperCase();
  // No try/finally needed — file[Symbol.dispose]() runs here,
  // even if an error is thrown above.
}

Với tài nguyên bất đồng bộ (kết nối mạng, handle DB async), dùng await using với [Symbol.asyncDispose]():

async function query() {
  await using conn = await db.connect(); // has [Symbol.asyncDispose]
  return conn.run('SELECT 1');
  // await conn[Symbol.asyncDispose]() runs (and is awaited) on exit.
}

Các mảnh hỗ trợ:

  • container gom nhiều tài nguyên và dọn tất cả cùng lúc (theo thứ tự ngược).
  • khi việc dọn dẹp throw trong lúc một lỗi khác đang lan, cả hai được giữ lại.

Ràng buộc: using / await using using / await using chỉ trong block — bên trong {}, vòng lặp, hay thân hàm — không dùng ở top-level của module.

Nó thay thế điệu nhảy dễ sai try { ... } finally { cleanup() } cho mọi tài nguyên có vòng đời.


Array.fromAsync — Array.from cho async iterable

Array.from không hiểu async iterable. Array.fromAsync thì có — nó await từng giá trị rồi gom lại.

async function* fetchPages() {
  yield await fetch('/api/page/1').then((r) => r.json());
  yield await fetch('/api/page/2').then((r) => r.json());
}

// await each yielded value, build an array
const pages = await Array.fromAsync(fetchPages());
console.log(pages.length); // 2

// also works on an array of promises:
const data = await Array.fromAsync([fetch('/a'), fetch('/b')]);

Đây là phiên bản async còn thiếu của Array.from, và xử lý giá trị tuần tự (khác Promise.all vốn song song).


Error.isError — Phát hiện Error đáng tin

value instanceof Error nói dối khi qua realm (iframe, worker, vm context) và có thể bị lừa bằng prototype giả. Error.isError() kiểm tra “nhãn” nội bộ thật sự.

Error.isError(new Error('x'));     // true
Error.isError(new TypeError('y')); // true

// Cases where instanceof fails but isError is correct:
Error.isError({ name: 'Error', message: 'fake' }); // false (just an object)
// An Error from another iframe/realm:
Error.isError(errorFromIframe);    // true (instanceof would be false)

Dùng nó trong thư viện, lớp chuẩn hóa lỗi, và bất kỳ đâu lỗi có thể vượt ranh giới context.


Cách dùng ngay hôm nay

  1. Kiểm tra hỗ trợ: phần lớn tính năng ES2025 đã có trong Chrome, Firefox, Safari, Node, Deno, Bun bản hiện tại. các mục ES2026 đang được tung ra nhưng hãy kiểm tra target tối thiểu của bạn.
  2. Transpile: TypeScript 5.2+ hỗ trợ using/await using; Babel hạ cấp được phần lớn cú pháp.
  3. Polyfill: core-js bao gồm Iterator Helpers, Set methods, Promise.try, Array.fromAsync,… cho runtime cũ.

Tham chiếu nhanh

Tính năngEditionTóm tắt
Iterator HelpersES2025Lazy .map/.filter/.take on iterators
Set methodsES2025union / intersection / difference / …
Promise.tryES2025Sync + async errors in one chain
RegExp.escapeES2025Escape strings for dynamic regex
Regex inline flagsES2025(?i:...) per-group modifiers
Import attributesES2025import x from './a.json' with { type: 'json' }
Float16ArrayES2025Half-precision typed array
using / await usingES2026Auto-dispose resources at scope exit
Array.fromAsyncES2026Array.from for async iterables
Error.isErrorES2026Realm-safe Error check

JavaScript tiếp tục hấp thụ những mẫu mà ta từng phải lấy từ thư viện — lặp lười, đại số tập hợp, dựng regex an toàn, dọn dẹp xác định. Càng ít dependency cho những thứ cơ bản này càng tốt.

Liên quan: