ES2022-2025实用特性盘点:让JavaScript代码更短更安全
日常写JS时,往往用顺手的那几招就够用了。但ECMAScript每年都会加一批小改进——不搞大新闻,却能让代码更短、更安全、更好读。这篇文章整理了一批近几年落地的实用特性,并配上代码示例,方便你在真实项目里直接用起来。
一、ES2022:少写废话,多写意图
1. 顶层await
解决啥问题:以前在模块顶层想「先拉配置再往下跑」,只能包一层async函数再调用,多一层壳。
以前常见写法:
async function bootstrap() {
const settings = await loadSettings();
runApp(settings);
}
bootstrap();现在可以这样:
const settings = await loadSettings();
runApp(settings);模块顶层直接await,启动逻辑更直观,少一点仪式感。
2. 私有字段(#)
解决啥问题:JS一直没真正的类私有字段,大家用_private之类的约定,本质上还是「君子协定」。
现在可以这样:
class TaskQueue {
#pending = [];
enqueue(task) {
this.#pending.push(task);
}
get size() {
return this.#pending.length;
}
}在类外访问instance.#pending会直接报错,封装更实在。
3. Error的cause
解决啥问题:一层层往上抛错时,原始错误容易丢,排查时要猜「到底是哪一环出的问题」。
现在可以这样:
try {
await fetchFromAPI();
} catch (e) {
throw new Error('请求失败', { cause: e });
}日志和DevTools里能看到完整错误链,定位更快。
4. Object.hasOwn()
解决啥问题:判断「是不是自有属性」以前要写Object.prototype.hasOwnProperty.call(obj, 'key'),又长又容易在边缘情况踩坑。
以前:
if (Object.prototype.hasOwnProperty.call(config, 'env')) {
useEnv(config.env);
}现在:
if (Object.hasOwn(config, 'env')) {
useEnv(config.env);
}语义清晰,写法也干净。
5. 数组的.at()(负索引)
解决啥问题:取「最后一个元素」总要写arr[arr.length - 1],取倒数第二个更啰嗦。
以前:
const last = items[items.length - 1];
const secondLast = items[items.length - 2];现在:
const last = items.at(-1);
const secondLast = items.at(-2);负索引从末尾算起,可读性更好。
二、ES2023:少改原数据,多返回新数据
这一批的主线是:尽量不原地修改,避免共享引用带来的隐蔽bug。
6. toSorted() / toReversed() / toSpliced()
解决啥问题:sort()、reverse()、splice()都会改原数组,一不留神就影响到别处。很多人只好先[...arr].sort(),语义不直接。
以前常见写法:
const byDate = [...list].sort((a, b) => a.date - b.date);
const reversed = [...list].reverse();现在:
const byDate = list.toSorted((a, b) => a.date - b.date);
const reversed = list.toReversed();
const withoutSecond = list.toSpliced(1, 1); // 去掉下标1的一个元素,返回新数组原数组不变,返回新数组,更适合状态管理和函数式写法。
7. findLast() / findLastIndex()
解决啥问题:只有find时,要找「最后一个满足条件的」要么自己倒着循环,要么[...arr].reverse().find(fn),既啰嗦又可能误用reverse。
以前:
const lastEven = [...nums].reverse().find((n) => n % 2 === 0);现在:
const lastEven = nums.findLast((n) => n % 2 === 0);
const lastEvenIndex = nums.findLastIndex((n) => n % 2 === 0);意图一眼能看懂。
三、ES2024:数据分组与异步控制
8. Object.groupBy()
解决啥问题:按某个键把数组分组,以前要么手写reduce,要么自己封一个groupBy工具函数。
以前:
const byStatus = list.reduce((acc, item) => {
const k = item.status;
if (!acc[k]) acc[k] = [];
acc[k].push(item);
return acc;
}, {});现在:
const byStatus = Object.groupBy(list, (item) => item.status);注意:键会变成字符串。若要用对象/其他类型当键,用Map.groupBy()。
9. Promise.withResolvers()
解决啥问题:需要「先拿到resolve/reject,稍后再调用」时,以前要在外层声明变量再塞进new Promise,写法别扭。
以前:
let resolve;
const done = new Promise((r) => { resolve = r; });
// 某事件触发后:resolve(value);现在:
const { promise, resolve, reject } = Promise.withResolvers();
// 某事件触发后:resolve(value);事件、队列、超时等场景里会清爽很多。
10. 可调整大小的ArrayBuffer
解决啥问题:以前ArrayBuffer长度固定,流式或动态长度时要自己管理多段buffer。
现在可以指定最大长度:
const buffer = new ArrayBuffer(256, { maxByteLength: 4096 });
// 后续可通过.resize()在256~4096之间调整适合流式、Worker、二进制协议等场景。
四、ES2025:迭代器、集合与安全正则
11. 迭代器上的map / filter / take
解决啥问题:数组链式map → filter → slice会每一步都生成新数组,数据量大时浪费内存。迭代器可以「按需计算」,不先占满内存。
以前(每一步都是新数组):
const result = data.map((x) => x.value).filter((v) => v > 0).slice(0, 10);现在(懒求值,用迭代器):
const result = data.values()
.map((x) => x.value)
.filter((v) => v > 0)
.take(10)
.toArray();适合大列表、流式数据或无限序列。注意:这里的map/filter/take是迭代器上的方法,和Array.prototype上的不同,需要先拿迭代器(如arr.values())。
12. Set的intersection / union / difference
解决啥问题:求交集、并集、差集以前要自己用filter、has拼,或者转成数组再算。
以前:
const both = new Set([...a].filter((x) => b.has(x)));
const all = new Set([...a, ...b]);现在:
const both = a.intersection(b);
const all = a.union(b);
const onlyInA = a.difference(b);集合运算更贴近数学表达,可读性更好。
13. RegExp.escape()
解决啥问题:用用户输入拼正则时,输入里若有( ) [ ] * + ?等会变成正则语法,既容易报错也有安全风险。以前要自己写一长串replace转义。
现在:
const safe = RegExp.escape(userInput);
const re = new RegExp(safe, 'i');动态正则更安全,也省得自己维护转义规则。
14. Promise.try()
解决啥问题:有的函数可能同步抛错、也可能返回Promise,想统一用await包一层时,要自己区分sync/async。Promise.try把「可能抛错的同步/异步逻辑」统一成Promise。
示例:
const result = await Promise.try(() => maybeSyncOrAsync());这样无论函数是同步抛错还是返回Promise,都能用同一套错误处理。
15. Float16Array
解决啥问题:以前只有Float32/Float64,在WebGPU、部分ML或紧凑二进制格式里需要16位浮点。
现在:
const data = new Float16Array(1024);更省内存,也方便和GPU/跨语言二进制格式对接。
五、整体在往哪走
拉远一点看,这几年JS的更新有一个共同方向:
少改原数据:toSorted、toReversed、toSpliced、findLast等,都在鼓励「返回新的」而不是「改原来的」
意图更明确:hasOwn、at(-1)、groupBy、Set的intersection/union/difference,名字即语义
异步更稳:顶层await、Error.cause、Promise.withResolvers、Promise.try,让异步和错误链更清晰
更安全、更省资源:私有字段、RegExp.escape、迭代器懒求值、Float16Array,分别在封装、安全、性能和内存上补了一块
不必追求「全背下来」——用到时查一下MDN或Node文档即可。更值得做的是:知道有这些能力存在,下次写reduce分组、取最后一个元素、或「不改变原数组的排序」时,能想到用新API,代码会更好维护。
如果你有固定用的Node版本,可以先在Node兼容表或caniuse上看下目标环境是否支持;部分ES2025特性需要较新的运行时(如Node 22+)。按需选用,就能把这些「悄悄好用」的特性真正用起来。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!