JavaScript 现代特性速览:ES2023 到 ES2026

JavaScript 的演进比大多数人印象中更快:ECMAScript 现在每年 6 月发布一个新版本,且大量实用特性不再需要等待"大版本",而是逐个落地并快速进入 Baseline。对前端工程师而言,掌握这些原生能力,可以显著减少对第三方工具库的依赖,也让代码更易读、更接近语言本身的设计意图。

这些能力是前端搭建的日常基础。搭配TypeScript 实战指南使用体验最佳;若想了解工程化侧的工具链,可阅读前端工具链 2026

数组与对象的"复制式"方法

ES2023 引入了 Change Array by Copy 系列(MDN 已标记为 Baseline Widely available):toSorted()toReversed()toSpliced()with()。它们与 sort/reverse/splice 对应,但不修改原数组、返回新数组,让不可变更新(配合 React、Zustand 等)写起来更顺手:

const months = ['Mar', 'Jan', 'Feb', 'Dec'];
const sorted = months.toSorted();        // ['Dec', 'Feb', 'Jan', 'Mar']
const reversed = months.toReversed();
const next = months.with(0, 'May');      // 替换索引 0 的元素
console.log(months);                      // 原数组不变

ES2024 又补齐了 Object.groupBy()Map.groupBy(),按回调返回的键对数组分组,替代手写 reduce 分组逻辑:

const byGrade = Object.groupBy(students, s => s.score >= 60 ? 'pass' : 'fail');

四者的对应关系可以这样记忆:在新方法名前加 to,或用 with 替代直接索引赋值。它们都返回新数组、原数组保持不变;Map.groupByObject.groupBy 的唯一区别是返回 Map 而非普通对象,适合需要保留数字键或自定义键类型的场景。

旧方法 新复制式方法 返回
sort() toSorted() 新数组
reverse() toReversed() 新数组
splice() toSpliced() 新数组
arr[i] = v with(i, v) 新数组

异步与 Promise 相关增强

  • Promise.withResolvers()(ES2024):一次调用同时拿到 promise、resolve、reject,避免在构造器外无法解析的"回调外包"问题;
  • Promise.try()(ES2025):把"可能同步、也可能异步"的函数统一包成 Promise,无需 async 包裹,错误处理更一致;
  • Array.fromAsync()(ES2024):从异步可迭代对象(如流式接口)直接构造数组。

正则与字符串增强

  • v 标志(unicodeSets,ES2024):字符串属性、集合差集/交集/并集等更强大的字符类运算,例如 [\p{Script=Han}&&\p{Letter}] 取汉字且为字母的字符;
  • RegExp.escape()(ES2025):转义用户输入,避免在动态构造正则时破坏语法;
  • String.prototype.isWellFormed() / toWellFormed()(ES2024):检测与修复孤立代理项,处理来自外部系统的乱码字符串;
  • JSON.parse 支持带 source 选项(ES2025),可还原原始文本。

迭代器与工具方法

ES2025 的 Iterator Helpers 让迭代器终于拥有 map/filter/take/drop/reduce/toArray 等方法,配合生成器可写出更声明式的数据流:

function* fib() { /* ... */ }
const first10 = [...fib()].filter(n => n % 2 === 0).toArray();

注意原生 Array.prototype.filter 返回数组,而迭代器版本返回新的迭代器,可继续链式调用,配合 take(5) 还能做到惰性计算

值得关注的 TC39 提案

截至 2026 年中,以下提案处于 Stage 3(接近落地)或 Stage 2.7,值得提前了解(详见 TC39 proposals 仓库):

  • Stage 3Iterator 相关(chunking、join、includes)、正则缓冲区边界 \A/\z/\ZError Stack Accessor、Await Dictionary、动态代码品牌检查;
  • Stage 2.7:Decorators(装饰器,含元数据)、ShadowRealm(隔离的 JS 执行环境)、ESM Phase Imports、不可变 ArrayBuffer
  • Stage 2:管道操作符、Math.clamp()Iterator.range()JSON.parseImmutable()StructsAmount(任意精度十进制金额)等;
  • Temporal:日期时间提案,目前处于 Stage 3,目标是取代 Date 的不合理设计,提供时区、日历、Duration 等完善的时间 API,适合日期敏感的应用提前调研。

如何在项目中安全采用

  1. 看 Baseline 标记:MDN 对每个特性标注 "Baseline Widely available",未达标前不直接用于生产;
  2. 交给编译器:Babel/TypeScript 会把较新语法转译到旧环境,core-js 负责 polyfill 缺失的内置方法(如 toSorted);
  3. 按需引入:能用 Object.groupBy 就不引 lodash 的分组函数,减小包体积;
  4. 配合 TS 类型:TypeScript 的 lib 配置会同步暴露新方法的类型定义(详见TypeScript 指南)。

参考:https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility

一个真实场景:状态更新与数据管道

把新特性放进真实代码里最能体会价值。以 React 的状态更新为例,过去"不改原数组"需要先拷贝再修改,现在用复制式方法直接写:

const [items, setItems] = useState(['Mar', 'Jan', 'Feb']);

// 排序,但不影响原数组
const sorted = items.toSorted();
// 替换某个索引
setItems(items.with(1, 'May'));

配合 Object.groupBy,一次请求拿到的数据可以先分组再渲染,不必引入 lodash:

const orders = await fetch('/api/orders').then(r => r.json());
const byStatus = Object.groupBy(orders, o => o.status);
// byStatus = { paid: [...], pending: [...], refunded: [...] }

迭代器方法在处理"无限流"时尤其有用:take 让管道在拿到足够数据后自动停止,不必先展开整个序列。比如从分页接口里懒加载,处理完一页即可 break,不用等全部数据落地。

常见问题

  • 新特性在生产环境能用吗? 看 MDN 的 Baseline 标记。只要标注 "Baseline Widely available",主流浏览器就无需 polyfill;仍不放心可以用 core-js 补齐后再上线。
  • toSortedsort 性能差多少? 复制式方法多一次数组拷贝,对百万级元素有可感知的开销;但绝大多数前端数据量远达不到这个规模,为了不可变性多花一点内存通常值得。
  • Object.groupBy 和手写 reduce 选哪个? 语义相同的前提下优先原生方法——代码更短、意图更清楚;只有需要自定义分组对象原型或做复杂聚合时才值得手写。
  • 升级新语法后老浏览器会崩吗? 语法层面交给 Babel/TS 转译,内置方法交给 polyfill;建议在 CI 里用 browserslist 固定目标浏览器,让 core-js 按需自动注入。

16IDC 观察

语言特性每落地一批,前端代码的"基础设施负债"就少一分。对独立开发者与小型团队来说,优先吃透原生能力、再决定是否引入库,往往是包体积、可维护性与招聘门槛之间的最佳平衡点。做多语言站点时,IntlTemporal 这类标准 API 尤其值得优先掌握。

原文来源:https://github.com/tc39/proposals