ECMAScript 2021 (简称 ES12)是 2021 年 6 月正式发布。主要聚焦于代码可读性、简化常见操作及增强异步处理能力。
一、 String.prototype.replaceAll()
传统的 String.prototype.replace() 方法在使用字符串作为匹配模式时,只替换第一个匹配项;若想替换所有的匹配,必须带 g 标签,不够直观。现在使用 replaceAll() 创建替换所有匹配到的子串,无需依赖正则 g 标志。
如果使用正则表达式作为模式, replaceAll 要求必须携带 g 标志,否则将抛出错误
const LMC = 'Love_my_China';
const _ = console.log;
// 使用 replace 仅能替换第一个
_(LMC.replace('_', ' ')); // 'Love my_China'
// 使用 replaceAll 替换所有
_(LMC.replaceAll('_', '')); // 'Love my China'
// 但是使用正则作为第一参数时,则必须携带 `g`
_(LMC.replaceAll(/_/g, ' ')); // 'Love my China'
二、 逻辑赋值运算符
简化了 “先判断条件,再赋值” 的常见逻辑。
| 运算符 | 含义 | 等价于 |
|---|---|---|
x &&= y | 若 x 为真 (truthy),则 x = y | x = x && y; |
x ||= y | 若 x 为假(falsy),则 x = y | x = x || y; |
x ??= y | 若 x 为 null 或是 undefined 时,则 x = y | x = x ?? y |
||= 针对假值,包括 0、''、false、undefined、null ,而 ??= 仅针对空值(空字符串不算空,包括 null/undefined)
// `&&=` 仅当左侧为真时赋值
let obj = { name: 'Tom' };
obj.name &&= 'Jerry'; // 赋值成功
obj.age &&= 20; // 赋值失败
// `||=` 仅当左侧为假时赋值
let msg = '';
msg ||= '爱我中华';
// `??=` 仅当左侧为 `null`/`undefined` 时赋值
let value = 0;
value ??= 10; // value === 0
value = null; // value === null
value ??= 20; // value === 20
三、 数字分隔符
在长数字(大整数、小数、二进制/十六进制)时可读性差,难以快速识别位数。
允许在数字中使用下划线 _ 作为分割符,增强可读性,而不影响数字的值。
// 整数(千分位)
const population = 4_200_000_000_000; // 四千二佰亿,比 4200000000000 易读
// 小数
const PI = 3.141_592_653;
// 二进制
const binary = 0b1001_1101_1011;
// 十六进制
const hex = 0x1f_3a_7d;
需要注意的是:
- 不能在数字开头/结尾使用 (如
_123或123_) - 不能在小数点的两侧使用 (如
12._34或12_.34) - 不能在科学计数法中
e/E两侧使用 (如1e_3)
四、 Promise.any 与 AggregateError
现有的 Promise 方法无法满足“等待第一个成功的 Promise” 的需求。Promise.all() 需要所有的 Promise 成功,Promise.race() 等待第一个状态的更改(无论成功还是失败)。新增 Promise.any() 接受一组 Promise,返回第一个 成功兑现(fulfilled) 的 Promise 结果;若所有的 Promise 都拒绝(rejected),则抛出 AggregateError (新类型错误,包含所有的拒绝原因)。
// 模拟三个异步请求,其中第二个成功
const promise1 = Promise.reject('请求1失败');
const promise2 = Promise.resolve('请求2成功');
const promise3 = Promise.reject('请求3失败');
// 等待第一个成功的 Promise
Promise.any([promise1, promise2, promise3])
.then(result => console.log(result)) // 输出:"请求2成功"
.catch(error => console.error(error));
// 所有 Promise 都失败的情况
Promise.any([promise1, promise3]).catch(error => {
console.log(error instanceof AggregateError); // true
console.log(error.errors); // ["请求1失败", "请求3失败"]
});
五、 WeakRef 与 FinalizationRegistry
JavaScript 中对象默认被“强引用”,只要存在强引用,垃圾回收(GC)就不会回收该对象。但某些场景(如缓存)需要“弱引用”--当对象没有被强引用时,允许被 GC 回收,避免内存泄漏。
WeakRef:创建对象的弱引用,不阻止 GC 回收FinalizationRegistry:注册一个回调函数,当对象被 GC 回收后触发,用于清理关联资源
// 创建弱引用
let obj = { data: '需要缓存的数据' };
/** 弱引用 obj */
const weakRef = new WeakRef(obj);
// 清除强引用,obj 现在仅被弱引用
obj = null;
// 尝试获取弱引用的对象(若未被回收则返回,否则返回 undefined)
// 可能输出 "需要缓存的数据"(如果当前尚未触发 GC)或 undefined(若依然触发了 GC)
console.log(weakRef.deref()?.data);
// 注册回收回调
const registry = new FinalizationRegistry(value => {
console.log(`对象已被处理,关联值:${value}`);
});
let cacheObj = { id: 1 };
// 注册对象和关联值(当 cacheObj 被回收时,回调会收到 "cache 4")
registry.register(cacheObj, 'cache 4');
// 清理强引用,触发 GC 后会执行
cacheObj = null;
- 弱引用的对象是否回收由 GC 决定,时机不可预,因此不能依赖
WeakRef.deref()一定有返回值; - 主要用于缓存、大型对象临时储存等场景,避免滥用(可能导致不可预测的行为)
六、 Array.prototype.sort() 优化:提升排序结果的一致性
Array.prototype.sort() 是 JavaScript 中用于数组排序的原型方法。在 ES12 之前,该方法的行为存在 “实现定义” (implementation-defined)的问题:对于某些特殊数组(如包含相同元素的数组),不同引擎的排序结果可能不一致。ES12 对 sort() 方法的算法进行了优化, 更精确地定义了排序行为 ,减少了这种不一致的情况。
优化内容:
- 对于数值数组:使用更稳定的排序算法(如快速排序的变种),确保相同的数值的元素顺序不变
- 对于字符串数组:使用字典排序(lexicographical order)排序,确保不同引擎结果一致
- 对于对象数组:根据
toString()方法的返回值排序,确保行为可预测
七、 国际化 API 增强
ES2021 新增了 Intl 对象,提供了更好的本地化支持。
Intl.ListFormat
提供语言敏感的列表格式化功能。它可以根据不同语言和地区规则,将数组元素格式化为自然语言的列表。
const fruits = ['苹果', '香蕉', '橘子'];
const formatter = new Intl.ListFormat('zh', {
style: 'long', // 其他可选: `'short'`、`'narrow'`
type: 'conjunction', // 其他可选: `'disjunction'`、`'unit'`
});
// "苹果、香蕉和橙子"
console.log(formatter.format(fruits));
// style 值为 'narrow' 时,打印为:"苹果、香蕉、橘子"
// type 值为 'disjunction' 时,打印为:"苹果、香蕉或橘子"
// type 值为 'unit' 时,打印为:"苹果香蕉橘子"
Intl.DateTimeFormat 的 dateStyle 和 timeStyle:
简化了日期和时间的格式化。开发者无需再记忆复杂的格式字符串,只需指定 dateStyle 和 timeStyle 选项(如 full、long、medium、short),API 会自动生成符合当前区域设置的格式。
const date = new Date();
const formatter = new Intl.DateTimeFormat('zh', {
dateStyle: 'full', // 年月日星期
timeStyle: 'long', // 时分秒时区
});
// 2025年10月15日星期三 GMT+8 21:11:26
console.log(formatter.format(date));