📋 目錄
這是 TC39 新語法深度解析系列的第七篇。陣列分組是超高頻操作——把訂單按狀態分組、把事件按日期分組、把使用者按角色分組。過去這需要
reduce或 lodash_.groupBy。現在,Object.groupBy讓這件事變成一行。
過去的做法
const orders = [
{ id: 1, status: 'pending', amount: 100 },
{ id: 2, status: 'completed', amount: 200 },
{ id: 3, status: 'pending', amount: 50 },
{ id: 4, status: 'refunded', amount: 200 },
];
// 過去:reduce 黑魔法
const byStatus = orders.reduce((groups, order) => {
const key = order.status;
groups[key] ??= [];
groups[key].push(order);
return groups;
}, {});
// { pending: [...], completed: [...], refunded: [...] }
這個 reduce 模式到處重複,現在可以一行:
const byStatus = Object.groupBy(orders, order => order.status);
Object.groupBy vs Map.groupBy
// Object.groupBy:返回普通物件(key 為字串)
const byStatus = Object.groupBy(orders, order => order.status);
// { pending: [...], completed: [...], refunded: [...] }
// Map.groupBy:返回 Map(key 可以是任何類型)
const byAmount = Map.groupBy(orders, order => order.amount > 100 ? 'expensive' : 'cheap');
// Map { 'expensive' => [...], 'cheap' => [...] }
什麼時候用哪個:
// 用 Object.groupBy:
// - key 是字串或可轉字串的值
// - 結果需要用 . 語法取值(group.pending)
// 用 Map.groupBy:
// - key 是數字、boolean、Date 等非字串
// - 需要 Map 的方法(has、forEach)
// - key 是 undefined 或 null
實際應用
事件日曆
const events = [
{ title: 'Team Meeting', date: '2026-03-19' },
{ title: 'Project Review', date: '2026-03-20' },
{ title: 'All Hands', date: '2026-03-19' },
{ title: 'Sprint Planning', date: '2026-03-22' },
];
// 按日期分組
const byDate = Object.groupBy(events, e => e.date);
// {
// '2026-03-19': [Meeting, All Hands],
// '2026-03-20': [Project Review],
// '2026-03-22': [Sprint Planning]
// }
使用者角色管理
// 用 Map.groupBy(因為 role 是 string,通常用 Object)
// 但如果需要分組後「是否存在某個 role」
const roles = Object.groupBy(users, u => u.role);
if (roles.admin) {
// 管理員數量
console.log(roles.admin.length);
}
Null 原型 Gotcha
Object.groupBy 返回的物件沒有 prototype(Object.create(null)):
const result = Object.groupBy(orders, o => o.status);
console.log(result.hasOwnProperty === undefined); // true
console.log(result.constructor); // undefined
console.log(result['pending']); // 正常,但 Object 原型方法不存在
這個設計是故意的——防止 prototype 污染攻擊。如果需要原型方法:
const result = Object.groupBy(orders, o => o.status);
const safeResult = Object.assign(Object.create(null), result);
console.log(safeResult['pending']); // 仍然正常
與 lodash _.groupBy 的比較
import { groupBy } from 'lodash';
// lodash
const byStatus = groupBy(orders, 'status'); // 指定 key 字串
// Object.groupBy
const byStatus = Object.groupBy(orders, o => o.status); // 需要函式
Object.groupBy 比 lodash 更輕量(無需 library),但 lodash.groupBy 仍然支援字串作為 key selector(groupBy(arr, 'fieldName')),這在 lodash 中仍然有用。
瀏覽器支援
ES2024——所有主流瀏覽器與 Node.js 21+ 都已原生支援:
// 可以在產品環境直接使用
const grouped = Object.groupBy(data, item => item.category);
總結
Object.groupBy 是一個很小的語法,但它的價值是消除一個重複出現的樣板程式碼:
// 從此告別
const grouped = items.reduce((acc, item) => {
const key = item.field;
(acc[key] ??= []).push(item);
return acc;
}, {});
// 直接
const grouped = Object.groupBy(items, item => item.field);
這是 TC39 新語法深度解析系列的第七篇。下一篇:Pattern Matching — match 表達式讓條件判斷更有表達力。
延伸閱讀
- TypeScript 6.0 RC 升級指南 — 支援 Object.groupBy 新語法
本文基於 ES2024 Object.groupBy / Map.groupBy(Stage 4)整理。