JavaScript ES2025+: Cú pháp mới, array methods và patterns thay thế lodash

2 tháng trước · 6 phút đọc
Mình từng import lodash vào mọi dự án
Hồi mới làm việc, mình nghĩ lodash là một phần không thể thiếu của JavaScript. _.groupBy, _.sortBy, _.cloneDeep... cứ cần gì là npm install lodash. Hậu quả? Bundle size phình to, thêm một dependency phải maintain, và đặc biệt là toàn bộ team phải biết lodash API.
Rồi ES2015 ra Array.prototype.find(). ES2019 ra flat(), flatMap(). Và bây giờ, ES2023-2025 đang dần lấp đầy những chỗ trống cuối cùng. Thực tế là phần lớn các tính năng lodash phổ biến nhất giờ đã có native equivalent trong JavaScript.
Bài này mình sẽ đi qua các method mới nhất, so sánh code cũ vs mới, và chỉ khi nào vẫn nên giữ lodash.
Bundle lodash đầy đủ nặng ~70KB - không phải con số nhỏ cho một utility library
Object.groupBy() - method mình chờ nhất
Array.prototype.groupBy từng là đề xuất gây tranh cãi vì xung đột prototype với Sugar.js. Cuối cùng TC39 chọn cách an toàn hơn: đặt nó ở Object.groupBy() thay vì gắn trực tiếp vào Array. Kết quả ra mắt chính thức trong ES2024.
So sánh nhanh:
Một biến thể nữa là Map.groupBy() - hữu ích khi key không phải string:
Object.groupBy() thay thế hoàn toàn _.groupBy - không cần thêm dependency
Lưu ý nhỏ: object trả về từ Object.groupBy() có prototype là null, tức là không có hasOwnProperty hay các method thông thường. Nếu code của bạn check obj.hasOwnProperty(key), cần điều chỉnh lại.
.toSorted(), .toReversed(), .toSpliced() - sort mà không mutate
Đây là nhóm method mình dùng nhiều nhất sau khi cập nhật. Vấn đề cũ là sort() và reverse() mutate mảng gốc, gây bug âm thầm, đặc biệt trong React khi bạn sort state trực tiếp.
.toReversed() hoạt động tương tự:
Và .toSpliced() - phiên bản immutable của splice():
Mutation là nguồn gốc của nhiều bug React - immutable methods giải quyết tận gốc
Nếu bạn đang học JavaScript từ đầu và chưa quen khái niệm mutation vs immutable, khóa Lập Trình JavaScript Nâng Cao có giải thích chi tiết về reference types - phần nền tảng để hiểu tại sao các method này quan trọng.
.with() - thay phần tử tại index mà không mutate
Method nhỏ nhưng xịn. Trước đây để thay một phần tử trong mảng mà không mutate, bạn phải map hoặc spread khá dài:
.with() cũng hỗ trợ negative index như at():
Kết hợp .with(), .toSorted(), .toReversed() cho functional-style array manipulation không cần lodash
Chaining các immutable method:
Promise.withResolvers() và các cải tiến async
ES2024 mang đến Promise.withResolvers() - pattern mà trước đây bạn phải tự implement khá awkward:
Promise.withResolvers() giải quyết pattern deferred mà không cần wrapper library
Cải tiến khác đáng chú ý trong ES2024-2025:
ArrayBuffer.prototype.transfer() - move memory buffer mà không copy, quan trọng cho WebAssembly và Web Workers.
Regex /v flag (Unicode Sets) - thay thế /u, hỗ trợ set operations trong regex như [A--Z] (difference), [A&&Z] (intersection). Regex phức tạp giờ đọc được hơn nhiều.
Khi nào vẫn nên giữ lodash?
Mình không bảo bạn xóa lodash ngay khỏi mọi dự án. Có những trường hợp nó vẫn xứng đáng.
_.cloneDeep() - structuredClone() đã có native từ ES2022 và handle hầu hết cases. Nhưng nó không clone function, DOM nodes, hay circular reference phức tạp theo cách lodash làm. Nếu bạn clone objects có method, lodash vẫn tốt hơn.
_.debounce() và _.throttle() - không có native equivalent. Bạn phải tự implement hoặc dùng library nhỏ hơn như lodash-es (tree-shakeable) thay vì cả package.
_.merge() deep merge - Object.assign() chỉ shallow merge. structuredClone merge theo cách riêng. Nếu cần deep merge objects lồng nhau, lodash vẫn là lựa chọn đáng tin.
Không phải cuộc chiến thắng-thua - chọn đúng tool cho từng use case
Nếu vẫn muốn dùng lodash, hãy dùng lodash-es với tree-shaking thay vì import toàn bộ:
Tree-shaking với lodash-es giúp bundle size giảm đáng kể so với import default.
