Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 59 — ES2024+ Features You Should Know

Đã đọc 0/14 mục · còn khoảng 10 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Mở đầu — vì sao bài này quan trọng

Nếu bạn từng đọc một đoạn code JavaScript của đồng nghiệp viết năm 2026 và thấy những cú pháp lạ lẫm như array.findLast(), Object.groupBy(), hay một dấu chấm hỏi kỳ lạ trong khối try/catch, thì bài học này chính là dành cho bạn. JavaScript không còn là ngôn ngữ "đứng yên" như thời ES5 nữa. Kể từ khi ES2015 (ES6) ra đời, ngôn ngữ này được cập nhật mỗi năm một lần, và mỗi bản cập nhật mang theo những tính năng giúp code ngắn gọn hơn, an toàn hơn, dễ đọc hơn.

Vấn đề thực tế tại nhiều công ty Việt Nam là: lập trình viên học JavaScript từ vài năm trước, rồi dừng lại. Họ vẫn viết theo "thói quen ES2015", trong khi runtime (Node.js, trình duyệt, Bun, Deno) đã hỗ trợ hàng loạt tính năng mới gọn hơn rất nhiều. Kết quả là code dài dòng, dễ sinh bug, và khi đọc thư viện mã nguồn mở hiện đại thì lúng túng vì không hiểu cú pháp.

Mục tiêu của bài này: giúp bạn nắm được cách JavaScript tiến hóa (quy trình TC39), và những tính năng quan trọng nhất từ ES2024 đến 2026 mà bạn nên dùng ngay trong công việc. Đây không phải là danh sách "cho biết", mà là những thứ bạn sẽ gõ hằng ngày.

Khái niệm cốt lõi

Quy trình TC39 — JavaScript tiến hóa như thế nào

JavaScript được chuẩn hóa bởi một ủy ban tên là TC39 (Technical Committee 39), gồm đại diện của các hãng lớn như Google, Apple, Microsoft, Mozilla. Mỗi tính năng mới (gọi là proposal — đề xuất) phải đi qua 5 giai đoạn:

  • Stage 0 (Strawperson): ý tưởng thô, mới được nêu ra.
  • Stage 1 (Proposal): ý tưởng được chấp nhận để nghiên cứu nghiêm túc, có người đứng ra "champion".
  • Stage 2 (Draft): đã có đặc tả chính thức bằng văn bản, cú pháp tương đối ổn định.
  • Stage 3 (Candidate): gần như hoàn chỉnh, chờ phản hồi từ thực tế triển khai. Các trình duyệt bắt đầu hỗ trợ thử.
  • Stage 4 (Finished): đã được duyệt, sẽ vào bản ES kế tiếp. Đây là lúc tính năng "chính thức" trở thành một phần của ngôn ngữ.
Điểm mấu chốt bạn cần nhớ: bản phát hành ES hằng năm (ES2024, ES2025...) chỉ gồm những proposal đã đạt Stage 4. Vì vậy, khi nghe "tính năng X đã vào ES2025", nghĩa là nó đã ổn định và an toàn để dùng trong production (miễn là runtime của bạn đủ mới).

ES2024 — những tính năng đáng dùng ngay

1. Object.groupBy() và Map.groupBy() — gom nhóm dữ liệu cực gọn. Trước đây để nhóm một mảng sản phẩm theo danh mục, bạn phải viết một vòng reduce rối rắm. Giờ thì:

const sanPham = [
  { ten: "Áo thun", loai: "quần áo" },
  { ten: "Quần jean", loai: "quần áo" },
  { ten: "Giày", loai: "phụ kiện" },
];
const theoLoai = Object.groupBy(sanPham, (sp) => sp.loai);
// { "quần áo": [...2 món], "phụ kiện": [...1 món] }

2. Promise.withResolvers() — tách resolve/reject ra ngoài. Thay vì phải tạo biến tạm trong constructor của Promise, bạn có ngay ba thứ cùng lúc:

const { promise, resolve, reject } = Promise.withResolvers();

Rất hữu ích khi bạn cần resolve một promise từ một chỗ khác trong code (ví dụ khi nhận sự kiện socket).

3. String.prototype.isWellFormed() và toWellFormed() — kiểm tra và sửa chuỗi Unicode bị lỗi (lone surrogate). Quan trọng khi xử lý dữ liệu người dùng nhập, đặc biệt với tiếng Việt và emoji.

4. ArrayBuffer.prototype.resize() — thay đổi kích thước buffer mà không cần tạo mới. Hữu ích cho xử lý dữ liệu nhị phân hiệu năng cao.

ES2023 nền tảng — đừng bỏ qua

Nhiều người nhảy thẳng tới ES2024 nhưng lại quên các "viên ngọc" của ES2023 vẫn cực kỳ thiết thực:

  • array.findLast() và findLastIndex(): tìm từ cuối mảng ngược lên. Rất tự nhiên khi bạn cần "giao dịch gần nhất thỏa điều kiện".
  • array.toSorted(), toReversed(), toSpliced(), with(): các phiên bản không làm thay đổi mảng gốc (immutable). Đây là cứu cánh lớn cho ai làm React/Vue, nơi việc mutate mảng gốc gây bug render khó chịu.
const diem = [7, 9, 5];
const daSapXep = diem.toSorted(); // [5, 7, 9], `diem` vẫn nguyên vẹn

ES2025 và xu hướng 2026

Tại thời điểm 2026, hai nhóm tính năng đáng chú ý:

1. Iterator Helpers — các phương thức .map(), .filter(), .take(), .drop() hoạt động lười (lazy) trên iterator, kể cả trên dữ liệu vô hạn:

function* soTuNhien() { let i = 1; while (true) yield i++; }
const ketqua = soTuNhien().filter((n) => n % 2 === 0).take(3).toArray();
// [2, 4, 6] — chỉ tính đúng 3 phần tử, không lặp vô hạn

2. Array.fromAsync() — duyệt async iterable thành mảng, cực gọn khi đọc dữ liệu theo dòng (stream).

3. Promise.try() — bọc một hàm (đồng bộ hoặc bất đồng bộ) vào trong một Promise một cách an toàn, kể cả khi hàm đó ném lỗi đồng bộ.

Một tính năng nữa đang ở Stage 3 và được kỳ vọng rất nhiều là Temporal — API mới thay thế hoàn toàn đối tượng Date đầy lỗi của JavaScript. Khi Temporal phổ biến, việc xử lý múi giờ (ví dụ chuyển đổi giữa giờ Việt Nam và giờ khách hàng ở Singapore) sẽ bớt đau đầu hẳn.

Tình huống thực tế

Tình huống 1: Tiki và bài toán gom nhóm đơn hàng

Giả sử đội backend của một sàn thương mại điện tử kiểu Tiki cần xây báo cáo: gom toàn bộ đơn hàng trong ngày theo trạng thái (chờ xử lý, đang giao, đã giao, hủy) để hiển thị dashboard cho bộ phận vận hành.

Trước đây, một bạn dev viết bằng reduce như sau, dài khoảng 12 dòng, và có một bug tinh tế: quên khởi tạo mảng rỗng nên thỉnh thoảng push vào undefined, gây crash dashboard lúc cao điểm 11h trưa. Sau khi nâng Node.js lên bản 21+, team chuyển sang:

const theoTrangThai = Object.groupBy(donHang, (dh) => dh.trangThai);

Một dòng. Không còn lỗi khởi tạo. Code review nhanh hơn, và quan trọng là người mới vào team đọc hiểu ngay. Bài học: tính năng mới không chỉ để "ngắn gọn cho vui" — nó loại bỏ cả một lớp bug do viết tay logic phổ biến.

Tình huống 2: MoMo và immutable array trong giao diện ví điện tử

Một đội frontend làm màn hình lịch sử giao dịch cho ứng dụng ví điện tử (kiểu MoMo) gặp bug: khi người dùng bấm "sắp xếp theo số tiền", danh sách bị sắp xếp vĩnh viễn và không quay lại được thứ tự thời gian gốc. Nguyên nhân: họ gọi array.sort(), vốn mutate trực tiếp mảng state của React, khiến React không nhận ra thay đổi và state gốc bị phá hỏng.

Cách sửa cũ là phải copy mảng: [...giaoDich].sort(). Nhưng nhiều bạn quên dấu .... Sau khi áp dụng ES2023, team đặt ra quy ước nội bộ: trong component React, cấm dùng .sort() và .reverse() trực tiếp, bắt buộc dùng .toSorted() và .toReversed(). Họ thêm một rule ESLint để tự động cảnh báo. Số lượng bug liên quan đến mutate state giảm gần như về 0 trong quý tiếp theo. Bài học: tính năng mới giúp bạn chọn mặc định an toàn, và kết hợp với linter sẽ tạo thành "hàng rào" bảo vệ cả team.

Tình huống 3: Startup fintech ở Singapore và Iterator Helpers

Một startup fintech tại Singapore xử lý log giao dịch khổng lồ — hàng triệu dòng mỗi ngày. Một tác vụ cần lấy ra 100 giao dịch đầu tiên vượt ngưỡng nghi vấn để đội rủi ro xem xét. Cách cũ là .filter() toàn bộ mảng rồi .slice(0, 100) — nhưng .filter() duyệt hết vài triệu dòng dù chỉ cần 100 cái đầu, gây tốn bộ nhớ và chậm.

Với Iterator Helpers, họ viết:

const nghiVan = logGiaoDich.values()
  .filter((g) => g.diemRuiRo > 80)
  .take(100)
  .toArray();

Vì các helper này hoạt động lazy, nó dừng ngay khi đủ 100 phần tử, không duyệt thừa. Thời gian xử lý giảm từ vài giây xuống vài chục mili-giây. Bài học: hiểu cơ chế lazy giúp bạn viết code vừa gọn vừa nhanh trên dữ liệu lớn.

Hướng dẫn từng bước

Để áp dụng tính năng ES2024+ một cách an toàn vào dự án thực tế, hãy làm theo trình tự sau:

  • Kiểm tra runtime đích. Tra cứu xem Node.js / trình duyệt / Bun bạn dùng có hỗ trợ tính năng đó chưa. Công cụ tốt nhất là trang caniuse.com (cho trình duyệt) và node.green (cho Node.js). Ví dụ Object.groupBy() cần Node.js 21+ hoặc Chrome 117+.
  • Xác định mục tiêu build (target). Nếu dùng TypeScript hoặc Babel/Vite, kiểm tra cấu hình target trong tsconfig.json (ví dụ "target": "ES2023"). Đặt target khớp với môi trường thực sự chạy code.
  • Thử nghiệm trong nhánh riêng. Đừng thay toàn bộ codebase ngay. Viết một tiện ích nhỏ dùng tính năng mới, chạy test, đảm bảo không lỗi trên môi trường CI.
  • Thêm polyfill nếu cần hỗ trợ runtime cũ. Với một số tính năng (như Array.fromAsync), nếu phải hỗ trợ trình duyệt cũ, dùng core-js để bù.
  • Thiết lập ESLint để khuyến khích cú pháp mới. Ví dụ thêm rule cảnh báo khi dùng .sort() trực tiếp trong React, gợi ý chuyển sang .toSorted().
  • Cập nhật tài liệu nội bộ. Ghi lại quy ước "ưu tiên dùng gì" để người mới không quay về thói quen cũ.

Lỗi thường gặp & mẹo

  • Dùng tính năng mà runtime chưa hỗ trợ. Lỗi kinh điển: viết Object.groupBy() chạy ngon trên máy bạn (Node 22), nhưng server production vẫn Node 18 nên crash với TypeError: Object.groupBy is not a function. Luôn kiểm tra phiên bản runtime của môi trường production, không phải máy cá nhân.
  • Nhầm groupBy trả về Object có prototype. Object.groupBy() trả về object không có prototype (null prototype) để tránh va chạm với key như "constructor". Đừng giả định nó có sẵn các method của Object thông thường.
  • Quên rằng toSorted vẫn so sánh theo chuỗi mặc định. Giống sort(), nếu không truyền hàm so sánh, [10, 2, 1].toSorted() cho ra [1, 10, 2] vì so theo ký tự. Luôn truyền (a, b) => a - b khi sắp xếp số.
  • Lạm dụng Iterator Helpers khi không cần lazy. Nếu dữ liệu nhỏ (vài chục phần tử), dùng .filter().map() thông thường trên mảng vẫn rõ ràng hơn. Iterator Helpers tỏa sáng với dữ liệu lớn hoặc vô hạn.
  • Mẹo: Khi đọc code thư viện hiện đại và gặp cú pháp lạ, hãy tra ngay tên tính năng kèm "TC39 proposal" trên Google. Bạn sẽ thấy giai đoạn (stage) và mức hỗ trợ, từ đó quyết định có nên dùng không.
  • Mẹo: Theo dõi repo tc39/proposals trên GitHub mỗi quý một lần. Chỉ cần 10 phút đọc lướt là bạn nắm được ngôn ngữ đang đi về đâu, không bị tụt hậu.

Bài tập thực hành

  • Gom nhóm: Cho một mảng nhân viên gồm { ten, phongBan, luong }. Dùng Object.groupBy() để nhóm theo phongBan, sau đó in ra số lượng nhân viên mỗi phòng.
  • Immutable sắp xếp: Cho mảng điểm thi [8, 5, 9, 6]. Tạo một mảng mới đã sắp xếp giảm dần bằng toSorted(), đồng thời chứng minh (bằng console.log) rằng mảng gốc không bị thay đổi.
  • Tìm từ cuối: Cho mảng giao dịch có { id, soTien, thanhCong }. Dùng findLast() để tìm giao dịch thành công gần nhất.
  • Iterator Helper: Viết một generator sinh số Fibonacci vô hạn, rồi dùng .filter() lấy các số chẵn và .take(5) để lấy 5 số Fibonacci chẵn đầu tiên.
  • Nghiên cứu mở rộng: Truy cập node.green, tra cứu xem Array.fromAsync được hỗ trợ từ phiên bản Node.js nào. Viết một đoạn ngắn dùng nó để đọc một async iterable đơn giản thành mảng.

Tóm tắt

JavaScript tiến hóa mỗi năm thông qua quy trình TC39 với 5 giai đoạn (Stage 0–4); chỉ những proposal đạt Stage 4 mới vào bản ES chính thức và an toàn để dùng trong production. Từ ES2023 đến 2026, những tính năng đáng dùng ngay gồm: các method immutable cho mảng (toSorted, toReversed, with), findLast/findLastIndex, Object.groupBy, Promise.withResolvers, Array.fromAsync, Promise.try, và đặc biệt là Iterator Helpers với khả năng xử lý lazy trên dữ liệu lớn. Tương lai gần là Temporal — chuẩn mới để xử lý ngày giờ và múi giờ.

Bài học cốt lõi: đừng dừng việc học JavaScript ở thời điểm bạn "biết đủ dùng". Luôn kiểm tra runtime đích trước khi áp dụng, kết hợp linter để biến cú pháp an toàn thành mặc định của cả team, và dành chút thời gian mỗi quý theo dõi các proposal mới. Đó là cách một lập trình viên giữ cho kỹ năng của mình không bị lỗi thời.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi