3045 字
15 分钟

ES2024新特性完全指南:Object.groupBy、Promise.withResolvers等核心功能详解(2026最新)

ECMAScript 2024(ES15)于2024年6月正式获批,带来了一系列实用的新特性。2026年今天,这些特性已在主流浏览器和Node.js中广泛支持。本文深度解析ES2024的核心新功能,并通过实际示例帮助你快速上手。

ES2024新特性完全指南

阅读提示

本文为ES2024专题指南,与 《TypeScript 5.x实用技巧完全指南》 互为补充:前者覆盖ES新特性,后者聚焦TypeScript高级技巧。


一、ES2024概览#

1.1 发布时间与背景#

  • 正式获批:2024年6月22日,第127届ECMA大会批准
  • 规范版本:ECMAScript 2024(第15版,ES15)
  • 提案流程:通过TC39委员会的4阶段提案流程

1.2 核心新特性一览#

特性提案阶段用途支持度
Object.groupByStage 4数据分组95%+
Map.groupByStage 4Map分组95%+
Promise.withResolversStage 4Promise创建90%+
ArrayBuffer resizableStage 4动态调整缓冲区90%+
ArrayBuffer transferStage 4转移缓冲区所有权90%+
RegExp v flagStage 4Unicode增强85%+
Atomics.waitAsyncStage 4异步等待85%+
Float16ArrayStage 4半精度浮点数80%+
Set methodsStage 4集合运算85%+

1.3 浏览器支持情况#

浏览器最低版本支持特性
Chrome122+全部支持
Firefox125+全部支持
Safari17.4+大部分支持
Edge122+全部支持
Node.js22+全部支持
Bun1.2+全部支持

二、Object.groupBy 与 Map.groupBy#

2.1 传统分组方式的问题#

场景:将用户按角色分组

// 传统方式:reduce
const users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'admin' },
{ name: 'David', role: 'guest' }
];
const grouped = users.reduce((acc, user) => {
const key = user.role;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(user);
return acc;
}, {});
console.log(grouped);
// { admin: [Alice, Charlie], user: [Bob], guest: [David] }

痛点

  • 代码冗长(5-6行)
  • 需要手动初始化数组
  • 容易出错(忘记初始化)

2.2 Object.groupBy#

Object.groupBy
const grouped = Object.groupBy(users, user => user.role);
console.log(grouped);
// { admin: [Alice, Charlie], user: [Bob], guest: [David] }
``
**语法**
```javascript
Object.groupBy(items, callbackFn)

参数

  • items:要分组的数组
  • callbackFn:分组回调函数,返回分组键

特点

  • 返回普通对象(键为字符串)
  • 无分组键时返回 undefined 属性
  • 简洁高效(一行代码)

2.3 Map.groupBy#

// 场景:按复杂对象分组
const products = [
{ name: 'iPhone', category: { id: 1, name: '手机' } },
{ name: 'MacBook', category: { id: 1, name: '手机' } },
{ name: 'iPad', category: { id: 2, name: '平板' } }
];
// Object.groupBy 无法处理对象键
const groupedObj = Object.groupBy(products, p => p.category);
// 问题:对象被转为字符串 "[object Object]"
// Map.groupBy 可以保留对象键
const groupedMap = Map.groupBy(products, p => p.category);
console.log(groupedMap);
// Map {
// { id: 1, name: '手机' } => [iPhone, MacBook],
// { id: 2, name: '平板' } => [iPad]
// }

语法

Map.groupBy(items, callbackFn)

特点

  • 返回 Map 对象
  • 键可以是任意类型(对象、函数等)
  • 保留键的原始类型

2.4 实际应用场景#

场景1:数据报表分组#

const orders = [
{ id: 1, status: 'completed', amount: 100 },
{ id: 2, status: 'pending', amount: 200 },
{ id: 3, status: 'completed', amount: 150 },
{ id: 4, status: 'cancelled', amount: 50 }
];
// 按状态分组统计
const byStatus = Object.groupBy(orders, o => o.status);
const stats = Object.fromEntries(
Object.entries(byStatus).map(([status, orders]) => [
status,
orders.reduce((sum, o) => sum + o.amount, 0)
])
);
console.log(stats);
// { completed: 250, pending: 200, cancelled: 50 }

场景2:按日期分组#

const logs = [
{ time: '2026-07-01', level: 'error' },
{ time: '2026-07-01', level: 'info' },
{ time: '2026-07-02', level: 'error' },
{ time: '2026-07-02', level: 'warn' }
];
const byDate = Object.groupBy(logs, log => log.time);
// { '2026-07-01': [...], '2026-07-02': [...] }

场景3:多字段分组#

const data = [
{ region: '华东', product: 'A', sales: 100 },
{ region: '华东', product: 'B', sales: 200 },
{ region: '华北', product: 'A', sales: 150 }
];
// 按区域+产品组合分组
const combined = Object.groupBy(data, d => `${d.region}-${d.product}`);
// { '华东-A': [...], '华东-B': [...], '华北-A': [...] }

2.5 Object.groupBy vs Map.groupBy#

维度Object.groupByMap.groupBy
返回类型普通对象Map对象
键类型仅字符串任意类型
键顺序无序有序(插入顺序)
性能略快略慢
适用场景简单分组复杂键类型

三、Promise.withResolvers#

3.1 传统Promise创建的问题#

// 传统方式:需要外部变量
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// 使用
setTimeout(() => {
resolve('done');
}, 1000);

痛点

  • resolve/reject 需要外部声明
  • 代码结构分散
  • 容易忘记初始化

3.2 Promise.withResolvers#

Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();
// 使用
setTimeout(() => {
resolve('done');
}, 1000);
promise.then(console.log); // 'done'

语法

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

特点

  • 一行代码创建Promise + resolve + reject
  • 无需外部变量
  • 代码更清晰

3.3 实际应用场景#

场景1:事件监听Promise化#

function waitForEvent(element, eventName) {
const { promise, resolve } = Promise.withResolvers();
element.addEventListener(eventName, (e) => {
resolve(e);
}, { once: true });
return promise;
}
// 使用
await waitForEvent(button, 'click');
console.log('Button clicked!');

场景2:超时控制#

function fetchWithTimeout(url, timeout = 5000) {
const { promise: timeoutPromise, resolve: timeoutResolve } = Promise.withResolvers();
const timeoutId = setTimeout(() => {
timeoutResolve(new Error('Timeout'));
}, timeout);
const fetchPromise = fetch(url).then(response => {
clearTimeout(timeoutId);
return response;
});
return Promise.race([fetchPromise, timeoutPromise]);
}

场景3:异步任务队列#

class AsyncTaskQueue {
constructor() {
this.queue = [];
this.pending = null;
}
add(task) {
const { promise, resolve } = Promise.withResolvers();
this.queue.push({ task, resolve });
this.process();
return promise;
}
async process() {
if (this.pending || this.queue.length === 0) return;
const { task, resolve } = this.queue.shift();
this.pending = task();
const result = await this.pending;
resolve(result);
this.pending = null;
this.process();
}
}

3.4 对比传统方式#

// 传统:5行
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// ES2024:1行
const { promise, resolve, reject } = Promise.withResolvers();

四、ArrayBuffer Resizable & Transfer#

4.1 传统ArrayBuffer的限制#

// 传统ArrayBuffer:固定大小
const buffer = new ArrayBuffer(1024);
// 无法调整大小
buffer.resize(2048); // TypeError: ArrayBuffer.resize is not a function

痛点

  • 创建后大小固定
  • 无法动态调整
  • 需要重新创建时复制数据

4.2 Resizable ArrayBuffer#

// ES2024:可调整大小的ArrayBuffer
const buffer = new ArrayBuffer(1024, { maxByteLength: 4096 });
console.log(buffer.resizable); // true
console.log.buffer.maxByteLength); // 4096
// 动态调整大小
buffer.resize(2048);
console.log(buffer.byteLength); // 2048
buffer.resize(512);
console.log(buffer.byteLength); // 512
// 不能超过maxByteLength
buffer.resize(8192); // RangeError

语法

new ArrayBuffer(length, { maxByteLength: maxLength })

特点

  • 创建时指定最大大小
  • resize() 动态调整
  • 调整大小时保留数据
  • 性能优于重新创建

4.3 Transfer ArrayBuffer#

// ES2024:转移所有权
const buffer1 = new ArrayBuffer(1024);
const view1 = new Uint8Array(buffer1);
view1[0] = 42;
// 转移到新buffer
const buffer2 = buffer1.transfer();
console.log(buffer1.byteLength); // 0(已分离)
console.log(buffer2.byteLength); // 1024
const view2 = new Uint8Array(buffer2);
console.log(view2[0]); // 42(数据保留)

语法

buffer.transfer()
buffer.transfer(newLength)

特点

  • 转移所有权,原buffer变为分离状态
  • 避免数据复制
  • 可指定新大小

4.4 实际应用场景#

场景1:动态数据处理#

// 数据量未知时动态调整
const buffer = new ArrayBuffer(1024, { maxByteLength: 1024 * 1024 });
let offset = 0;
function appendData(data) {
const bytes = new TextEncoder().encode(data);
if (offset + bytes.length > buffer.byteLength) {
buffer.resize(Math.min(buffer.byteLength * 2, buffer.maxByteLength));
}
new Uint8Array(buffer).set(bytes, offset);
offset += bytes.length;
}

场景2:WebWorker数据传递#

// 主线程
const buffer = new ArrayBuffer(1024);
const worker = new Worker('worker.js');
// 转移所有权给Worker
worker.postMessage({ buffer: buffer.transfer() }, []);
// buffer已分离,主线程无法访问
console.log(buffer.byteLength); // 0
// Worker线程
self.onmessage = (e) => {
const buffer = e.data.buffer;
const view = new Uint8Array(buffer);
// 处理数据...
// 转移回主线程
self.postMessage({ buffer: buffer.transfer() }, []);
};

4.5 SharedArrayBuffer 支持#

// SharedArrayBuffer也支持resizable
const sharedBuffer = new SharedArrayBuffer(1024, { maxByteLength: 4096 });
sharedBuffer.grow(2048);
console.log(sharedBuffer.byteLength); // 2048

注意

  • SharedArrayBuffer使用 grow() 而非 resize()
  • 只能增大,不能缩小
  • 需要安全上下文(HTTPS + COOP/COEP)

五、RegExp v Flag(Unicode增强)#

5.1 传统Unicode匹配的问题#

// 传统方式:u flag
const regex = /[\p{L}]/u; // 匹配任何字母
// 问题:集合操作复杂
// 匹配字母但排除汉字
const regex2 = /[\p{L}--[^\p{Script=Han}]]/u; // 语法不支持

5.2 v Flag特性#

// ES2024:v flag
const regex = /[\p{L}]/v;
// 集合操作:差集
const regex2 = /[\p{L}--\p{Script=Han}]/v;
// 集合操作:交集
const regex3 = /[\p{L&&\p{Script=Latin}]/v;
// 集合操作:并集
const regex4 = /[\p{L}\p{N}]/v; // 字母或数字

语法

/pattern/v

特点

  • 增强Unicode支持
  • 支持集合操作(交集、并集、差集)
  • 支持多字符字符串
  • 兼容u flag

5.3 集合操作详解#

操作语法说明
并集[A\B]A或B
交集[A&&B]A且B
差集[A--B]A但非B

5.4 实际应用场景#

场景1:匹配特定语言字符#

// 匹配中文汉字
const hanRegex = /\p{Script=Han}/v;
// 匹配英文字母(排除其他字母)
const latinRegex = /[\p{L}--\p{Script=Han}--\p{Script=Hiragana}]/v;

场景2:验证用户名#

// 用户名:字母、数字、下划线,但排除特殊符号
const usernameRegex = /^[\p{L}\p{N}_--[\p{S}\p{P}]]+$/v;

场景3:多字符匹配#

// 匹配表情符号
const emojiRegex = /\p{Emoji}/v;
// 匹配完整表情(包括组合)
const fullEmojiRegex = /\p{RGI_Emoji}/v;

六、Atomics.waitAsync#

6.1 传统Atomics.wait的限制#

// Atomics.wait:阻塞主线程
const sharedBuffer = new SharedArrayBuffer(4);
const view = new Int32Array(sharedBuffer);
// 阻塞等待(不能在主线程使用)
Atomics.wait(view, 0, 0, 1000);

痛点

  • 阻塞线程
  • 不能在主线程使用
  • 只能在Worker中使用

6.2 Atomics.waitAsync#

// ES2024:异步等待
const sharedBuffer = new SharedArrayBuffer(4);
const view = new Int32Array(sharedBuffer);
// 异步等待(不阻塞)
const result = Atomics.waitAsync(view, 0, 0, 1000);
console.log(result.async); // true
console.log(result.value); // Promise
await result.value;
// "ok" - 值已改变
// "timed-out" - 超时

语法

Atomics.waitAsync(typedArray, index, value, timeout)

特点

  • 非阻塞等待
  • 返回Promise
  • 可在主线程使用
  • 与notify配合使用

6.3 实际应用场景#

场景1:跨Worker同步#

// Worker 1:等待信号
const result = Atomics.waitAsync(view, 0, 0);
await result.value;
console.log('Received signal');
// Worker 2:发送信号
Atomics.store(view, 0, 1);
Atomics.notify(view, 0, 1);

场景2:生产者-消费者#

class AsyncQueue {
constructor(capacity) {
this.buffer = new SharedArrayBuffer(capacity * 4 + 8);
this.data = new Int32Array(this.buffer);
this.head = 0;
this.tail = capacity;
}
async consume() {
while (this.head === this.tail) {
await Atomics.waitAsync(this.data, 0, 0).value;
}
const item = this.data[this.head + 2];
this.head++;
Atomics.notify(this.data, 0, 1);
return item;
}
produce(item) {
while (this.head === this.tail) {
// 队列满,等待
}
this.data[this.tail + 2] = item;
this.tail++;
Atomics.notify(this.data, 0, 1);
}
}

七、Set Methods(集合运算)#

7.1 传统集合运算的问题#

// 传统方式:手动实现
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x));
// 并集
const union = new Set([...setA, ...setB]);
// 差集
const difference = new Set([...setA].filter(x => !setB.has(x));

7.2 ES2024 Set Methods#

// 交集
const intersection = setA.intersection(setB);
// Set { 2, 3 }
// 并集
const union = setA.union(setB);
// Set { 1, 2, 3, 4 }
// 差集
const difference = setA.difference(setB);
// Set { 1 }
// 对称差集(异或)
const symmetricDifference = setA.symmetricDifference(setB);
// Set { 1, 4 }
// 子集判断
setA.isSubsetOf(setB); // false
setA.isSubsetOf(setA); // true
// 超集判断
setA.isSupersetOf(setB); // false
// 无交集判断
setA.isDisjointFrom(setB); // false

7.3 方法一览#

方法语法返回类型
intersectiona.intersection(b)Set
uniona.union(b)Set
differencea.difference(b)Set
symmetricDifferencea.symmetricDifference(b)Set
isSubsetOfa.isSubsetOf(b)boolean
isSupersetOfa.isSupersetOf(b)boolean
isDisjointFroma.isDisjointFrom(b)boolean

八、Float16Array#

8.1 传统Float32Array的限制#

// Float32Array:32位浮点数
const f32 = new Float32Array([1.5, 2.5]);
// 每个元素占用4字节

8.2 Float16Array#

// ES2024:16位半精度浮点数
const f16 = new Float16Array([1.5, 2.5]);
// 每个元素占用2字节
console.log(f16.BYTES_PER_ELEMENT); // 2

特点

  • 节省50%内存
  • 精度较低(适合不需要高精度的场景)
  • 适合AI/ML场景(半精度足够)

8.3 应用场景#

  • 机器学习模型存储
  • 大规模数值数据
  • 内存受限环境

九、浏览器兼容性处理#

9.1 Polyfill方案#

// Object.groupBy polyfill
if (!Object.groupBy) {
Object.groupBy = function(items, callbackFn) {
const result = {};
for (const item of items) {
const key = callbackFn(item);
if (!result[key]) {
result[key] = [];
}
result[key].push(item);
}
return result;
};
}
// Map.groupBy polyfill
if (!Map.groupBy) {
Map.groupBy = function(items, callbackFn) {
const result = new Map();
for (const item of items) {
const key = callbackFn(item);
if (!result.has(key)) {
result.set(key, []);
}
result.get(key).push(item);
}
return result;
};
}
// Promise.withResolvers polyfill
if (!Promise.withResolvers) {
Promise.withResolvers = function() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}

9.2 Babel配置#

babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
chrome: '122',
firefox: '125',
safari: '17.4'
},
useBuiltIns: 'usage',
corejs: 3
}]
]
};

9.3 检测支持情况#

// 检测特性支持
const features = {
'Object.groupBy': typeof Object.groupBy === 'function',
'Map.groupBy': typeof Map.groupBy === 'function',
'Promise.withResolvers': typeof Promise.withResolvers === 'function',
'ArrayBuffer resizable': 'resizable' in ArrayBuffer.prototype,
'RegExp v flag': (() => {
try {
new RegExp('', 'v');
return true;
} catch {
return false;
}
})(),
'Atomics.waitAsync': typeof Atomics.waitAsync === 'function'
};
console.table(features);

十、总结#

ES2024带来了多个实用的新特性:

特性核心价值推荐指数
Object.groupBy简化数据分组⭐⭐⭐⭐⭐
Map.groupBy复杂键类型分组⭐⭐⭐⭐
Promise.withResolvers简化Promise创建⭐⭐⭐⭐⭐
ArrayBuffer resizable动态内存管理⭐⭐⭐⭐
RegExp v flagUnicode增强匹配⭐⭐⭐
Atomics.waitAsync异步同步原语⭐⭐⭐
Set methods集合运算简化⭐⭐⭐⭐
Float16Array内存优化⭐⭐⭐

行动建议

  1. 立即使用 Object.groupByPromise.withResolvers(支持度最高)
  2. 评估 ArrayBuffer resizable 用于动态数据处理场景
  3. 使用 Set methods 简化集合运算代码
  4. 为不支持的环境添加 polyfill

📚 相关文章推荐#


Sources#


:::info 免责声明 本文为技术信息分享,不构成任何建议。实际使用请根据项目需求和环境支持情况选择合适的特性。 :::

ES2024新特性完全指南:Object.groupBy、Promise.withResolvers等核心功能详解(2026最新)
https://971918.xyz/posts/docs/es2024-features-guide/
作者
九所长
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0