ES12(ES2021)新特性

发布时间:2021年6月 ES12 新增了逻辑赋值、数字分隔符、Promise.any()WeakRef 等特性。

1. 逻辑赋值运算符(Logical Assignment Operators)

把逻辑运算和赋值运算组合在一起:

||=(逻辑或赋值)

当左侧为假值时,才进行赋值:

let x = null;
x ||= 'default';  // x = 'default'

let y = 'hello';
y ||= 'default';  // y = 'hello'

let z = 0;
z ||= 100;        // z = 100,因为 0 是假值

大致可以理解为:

x || (x = 'default');

严格来说,规范实现会保证左值只求值一次;上面的写法只是便于理解语义。

&&=(逻辑与赋值)

当左侧为真值时,才进行赋值:

let x = 1;
x &&= 2;  // x = 2

let y = null;
y &&= 2;  // y = null

等价语义可理解为:

x && (x = 2);

??=(空值合并赋值)

当左侧值为 nullundefined 时,才赋值:

let x = null;
x ??= 'default';  // x = 'default'

let y = 0;
y ??= 'default';  // y = 0(0 不是 null/undefined)

等价语义可理解为:

if (x === null || x === undefined) {
  x = 'default';
}

三者对比

// ||=  在假值时赋值(null, undefined, 0, '', false, NaN)
// &&=  在真值时赋值
// ??=  只在 null/undefined 时赋值

let a = 0;
a ||= 100;   // 100

a = 0;
a &&= 100;   // 0

a = 0;
a ??= 100;   // 0

实际应用

let config = {};
config.theme ??= 'light';
config.debug ??= false;

let user = {};
user.name ||= '匿名用户';

2. 数字分隔符(Numeric Separators)

使用下划线 _ 分隔长数字,提高可读性:

基本用法

let price = 1_000_000;
let binary = 0b1010_0001_1100;
let color = 0xFF_FF_FF;
let num = 1.234_567_890e5;
let big = 1_000_000_000_000n;

规则

1__000;  // SyntaxError,不能连续多个下划线
100_;    // SyntaxError,不能在数字末尾
1_.0;    // SyntaxError,小数点前不能紧跟下划线
1._0;    // SyntaxError,小数点后不能紧跟下划线

适用场景

  • 金额、编号、时间戳等长整数
  • 二进制 / 十六进制常量
  • BigInt 大整数

3. Promise.any()

接收一组 Promise,返回第一个成功的结果:

与 Promise.race() 的区别

let p1 = new Promise((_, reject) => setTimeout(() => reject('失败1'), 100));
let p2 = new Promise(resolve => setTimeout(() => resolve('成功2'), 200));
let p3 = new Promise(resolve => setTimeout(() => resolve('成功3'), 300));

Promise.race([p1, p2, p3])
  .then(res => console.log(res))
  .catch(err => console.log(err));
// 输出:失败1

Promise.any([p1, p2, p3])
  .then(res => console.log(res));
// 输出:成功2

全部失败时

let p1 = Promise.reject('失败1');
let p2 = Promise.reject('失败2');

Promise.any([p1, p2]).catch(err => {
  console.log(err instanceof AggregateError);  // true
  console.log(err.errors);                     // ['失败1', '失败2']
  console.log(err.errors.length);              // 2
});

实际应用

async function fetchFastest(urls) {
  try {
    return await Promise.any(urls.map(url => fetch(url)));
  } catch (err) {
    console.log('所有源都失败了:', err.errors);
  }
}

Promise.any([
  db1.query('SELECT 1'),
  db2.query('SELECT 1'),
  db3.query('SELECT 1')
]).then(result => console.log('最快响应:', result));

4. WeakRef 和 FinalizationRegistry

WeakRef(弱引用)

创建对象的弱引用,不会阻止垃圾回收:

let obj = { data: 'important' };
let weakRef = new WeakRef(obj);

let cached = weakRef.deref();
if (cached) {
  console.log(cached.data);
}

obj = null;
// 之后对象可能被回收,weakRef.deref() 可能得到 undefined

使用场景:缓存

class Cache {
  #cache = new Map();

  set(key, value) {
    this.#cache.set(key, new WeakRef(value));
  }

  get(key) {
    let ref = this.#cache.get(key);
    if (!ref) return undefined;

    let value = ref.deref();
    if (value === undefined) {
      this.#cache.delete(key);
    }
    return value;
  }
}

FinalizationRegistry(终结注册)

对象被垃圾回收时,可触发注册回调:

let registry = new FinalizationRegistry((heldValue) => {
  console.log(`对象 ${heldValue} 已被回收`);
});

let obj = { name: '张三' };
registry.register(obj, 'user-张三');

obj = null;

注意事项

  • 垃圾回收时机不可预测
  • 不要依赖它做关键业务逻辑
  • WeakRefFinalizationRegistry 适合底层工具、缓存、资源管理等高级场景

5. String.prototype.replaceAll()

替换所有匹配项:

'hello world hello'.replace(/hello/g, 'hi');
// 'hi world hi'

'hello world hello'.replaceAll('hello', 'hi');
// 'hi world hi'

与 replace() 的区别

'aabbcc'.replace('b', 'x');     // 'aaxbcc'
'aabbcc'.replaceAll('b', 'x');  // 'aaxxcc'

注意

'aabb'.replaceAll(/b/, 'x');    // TypeError
'aabb'.replaceAll(/b/g, 'x');   // 'aaxx'
  • 传入正则时必须带 g 标志
  • 如果只是普通字符串替换,replaceAll() 会更直观

6. AggregateError

当多个错误需要一起抛出时,可以使用 AggregateError。在 ES2021 中,它最常见的场景就是 Promise.any() 全部失败时:

try {
  await Promise.any([
    Promise.reject(new Error('错误1')),
    Promise.reject(new Error('错误2'))
  ]);
} catch (err) {
  console.log(err instanceof AggregateError);  // true
  console.log(err.message);                    // 'All promises were rejected'
  console.log(err.errors);                     // [Error('错误1'), Error('错误2')]
}

手动创建 AggregateError

throw new AggregateError([
  new Error('缺少用户名'),
  new Error('缺少密码')
], '表单校验失败');

总结

特性 说明 重要性
` =, &&=, ??=`
数字分隔符 _ 提高长数字可读性 ⭐⭐⭐
Promise.any() 返回第一个成功的 Promise ⭐⭐⭐⭐
WeakRef 对象的弱引用 ⭐⭐
FinalizationRegistry 对象回收通知 ⭐⭐
String.replaceAll() 替换全部匹配项 ⭐⭐⭐⭐
AggregateError 聚合多个错误 ⭐⭐

ES2021 的亮点在于:一部分特性显著提升了日常代码可读性(逻辑赋值、数字分隔符、replaceAll()),另一部分则增强了异步与底层能力(Promise.any()WeakRefAggregateError)。

ES6+新特性 文章被收录于专栏

包含ES6+新特性

全部评论

相关推荐

03-04 14:31
门头沟学院 Java
点赞 评论 收藏
分享
今天周一休息,突发奇想写一篇阶段总结。如题,我已经去了一个和Java彻底毫无关联的行业。曾经我以为自己能在计算机行业发光发热,没想到刚入行一年多就当了逃兵。从最开始的热爱到现在一看到代码就厌恶,不知道自己经历了什么。所以我去干什么了?答案是:在成都当了租房销售。上班那会压力大了就念叨着去干租房中介,但是一直下不去这个决心,想着自己学了四年多的计算机知识,终究还是不甘心。终于在某一天准备八股文的时候,看着无数篇和工作内容关系不大的理论知识,那一刻下定决心,决定尝试一下销售行业,也算是给自己一个交代。后面阴差阳错的投了成都自如去当租房管家,没想到面试很顺利,在当天一百多个面试的人里面,我成为了为数不多通过的几个幸运儿之一。目前已经培训通过,正式入职,也开了单,也有压力但是每天过得很开心,真心喜欢那种和人交流的感觉,哪怕是最后没有选择找我租房。说这些也是想告诉那些大三,大四正在找Java实习而焦虑的同学:你们现在还年轻,选择很多,容错率也很高,可以尽情去尝试自己喜欢的行业和工作。不用因为某一次的面试没通过或者简历石沉大海而焦虑,更不用因为身边人都在挤编程的独木桥就强迫自己跟风。也算是自己的碎碎念吧,也希望自己能在新的领域取得一点小成就。也祝牛油工作顺利!
沉淀小子:干啥都不丢人啊,生存是必须要的,销售很考验一个人综合素质能力的,好的销售人脉和资源可不比写字楼的白领差啊
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务