13 min read

揭秘ES2021令人兴奋的语言特性

大家好!我是星辰编程理财。今天我分享一篇关于ES2021(ES12)的文章,它将介绍ES2021的语言特性和功能,包括WeakRefs、Logical assignment operators、Private methods and accessors (class fields)、Promise.allSettled()等等。通过故事形式以及详细的阐述和示例,带领大家一起探索这些特性的用处,作为刚入门的新手,它能让你能够在前端开发中游刃有余。废话不多说,让我们一起探索ES2021的语言特性和功能,开启前端开发的新征程吧!

String.prototype.replaceAll()

我们经常需要对字符串进行替换操作。在 ES2021 中,引入了 String.prototype.replaceAll() 方法,它为我们提供了一种简单而强大的替换字符串的方式。

有一次,我正在开发一个社交媒体应用,用户可以在评论区留言。然而,我们面临一个问题:用户有时会在留言中使用敏感词汇,这严重违反了我们的社区准则。我们需要在用户提交评论后,将敏感词汇替换成星号。

我决定使用 replaceAll() 方法来解决这个问题。首先,我定义了一个敏感词汇数组,包含了一些常见的敏感词汇。然后,当用户提交评论后,我使用 replaceAll() 方法遍历敏感词汇数组,并将评论中的敏感词汇替换成星号。

const sensitiveWords = ['敏感词1', '敏感词2', '敏感词3'];
const comment = '这个评论包含敏感词1和敏感词2,请注意!';

const filteredComment = comment.replaceAll(
  new RegExp(sensitiveWords.join('|'), 'gi'),
  '***'
);

console.log(filteredComment);
// 输出: 这个评论包含***和***,请注意!

通过使用 replaceAll() 方法,我能够轻松地将评论中的敏感词汇替换成了星号,这样就保护了我们的社区环境。这个方法非常简洁高效,让我能够更好地处理用户输入中的敏感内容。

Promise.any()

我们经常需要处理异步操作,而在 ES2021 中,引入了 Promise.any() 方法,它为我们提供了一种更灵活的处理多个 Promise 的方式。

有一次,我正在开发一个电商网站,其中一个功能是展示多个供应商的商品价格,并返回最低价格。我面临的问题是,每个供应商的价格请求都是异步的,我需要等待所有请求完成后,再找出最低价格。

在过去,我通常会使用 Promise.all() 来等待所有请求完成,然后进行价格比较。然而,这种方式有一个缺点,即一旦其中一个 Promise 被拒绝,整个链路就会被拒绝,导致无法获取最低价格。

幸运的是,在 ES2021 中引入了 Promise.any() 方法,它允许我们等待多个 Promise 中的任意一个被解决(resolved),并返回该 Promise 的结果。这为我解决了之前遇到的问题。

让我们来看一下我是如何使用 Promise.any() 方法来获取最低价格的例子:

const fetchPrices = async () => {
  const promises = [
    fetchPriceFromSupplier1(),
    fetchPriceFromSupplier2(),
    fetchPriceFromSupplier3()
  ];

  try {
    const lowestPrice = await Promise.any(promises);
    console.log(`最低价格是:${lowestPrice}`);
  } catch (error) {
    console.error('获取价格失败:', error);
  }
};

fetchPrices();

通过使用 Promise.any() 方法,我可以并行地发起多个价格请求,并在任何一个请求成功后立即获取到最低价格。这让我能够更高效地处理异步操作,并且不会受到某个请求失败的影响。

WeakRefs

我们经常需要处理对象的引用和内存管理。在 ES2021 中,引入了 WeakRefs 特性,它为我们提供了一种更好地处理弱引用的方式。

有一次,我正在开发一个图片库应用,用户可以上传图片并进行管理。然而,由于图片占用内存较大,我需要在用户不再需要某张图片时,自动释放内存。这样可以避免内存泄漏和浪费。

在过去,我通常会使用手动引用计数的方式来管理对象的引用和内存释放,但这种方式很容易出错,而且不够灵活。幸运的是,ES2021 引入了 WeakRefs,它为我解决了这个问题。

让我们来看一下我是如何使用 WeakRefs 来管理图片对象的例子:

class Image {
  constructor(url) {
    this.url = url;
    this.#ref = new WeakRef(this);
  }

  async load() {
    // 加载图片逻辑
  }

  release() {
    if (this.#ref.deref() === this) {
      // 释放图片内存
    }
  }
}

const image = new Image('https://example.com/image.jpg');
image.load();

// 用户不再需要这张图片
image.release();

通过使用 WeakRefs,我可以在图片对象不再被其他地方引用时,自动释放图片的内存。这样我就能够更好地管理内存,避免内存泄漏和浪费。

Logical assignment operators (||=, &&=, ??=)

我经常需要在处理变量赋值时进行一些逻辑操作。在 ES2021 中,引入了三个新的逻辑赋值运算符:||=&&=??=

有一天,我正在开发一个任务管理应用,用户可以创建任务并设置任务的状态。每当用户完成一个任务时,我需要将任务的状态标记为已完成。以前,我通常会使用条件语句来实现这个逻辑:

if (!task.status) {
  task.status = 'completed';
}

然而,这种方式显得冗长而且不够简洁。幸运的是,在 ES2021 中,我可以使用逻辑赋值运算符来简化这个过程:

task.status ||= 'completed';

通过使用 ||= 运算符,我可以在 task.status 为假值(如 nullundefined)时,将其赋值为 'completed'。这让我的代码更加简洁、易读,并且减少了冗余的条件判断。

类似地,&&= 运算符可以用于在变量为真值时进行赋值操作,而 ??= 运算符则可以用于在变量为 nullundefined 时进行赋值操作。这些逻辑赋值运算符让我能够以更简洁的方式处理变量赋值的逻辑,提升了代码的可读性和维护性。

Numeric separators

我经常需要处理大量的数字,例如金额、电话号码等。在 ES2021 中,引入了数字分隔符特性,它为我们提供了一种更可读且易于理解的数字表示方式。

有一次,我正在开发一个电商网站,其中一个功能是显示商品的价格。商品价格通常是一个较大的数字,例如 10000000,这种表示方式不够直观,不容易阅读。为了提高用户体验,我想将商品价格以更易读的方式呈现出来。

幸运的是,在 ES2021 中,我可以使用数字分隔符来增加数字的可读性。使用下划线 _ 作为数字分隔符,我可以将这个较大的数字表示为:10_000_000。这样的表示方式更加直观,让用户能够更容易地理解和识别数字的大小。

除了较大的数字外,数字分隔符还可以用于其他场景,例如电话号码、信用卡号码等。通过使用数字分隔符,我可以将这些长数字以一种更可读的方式展示给用户,提高了用户体验和界面的友好性。

Private methods and accessors (class fields)

我经常需要创建和维护复杂的类结构,以实现面向对象的编程。在 ES2021 中,引入了私有方法和访问器(私有字段)的特性,它为我们提供了一种更好地封装和保护类的内部实现细节的方式。

有一次,我正在开发一个音乐播放器应用,其中一个关键的类是 Player 类,用于控制音乐的播放。在过去,这个类的所有方法都是公开的,无论是用于内部使用的还是供外部调用的。这导致类的内部实现细节暴露给了外部,不够安全和可维护。

幸运的是,在 ES2021 中,我可以使用私有方法和访问器来解决这个问题。通过在方法名或字段名前加上 # 符号,我可以将它们标记为私有的,只能在类的内部进行访问。这样,我就能够更好地封装类的内部逻辑,保护了实现细节,同时提供了更清晰的接口供外部调用。

让我们来看一下我是如何使用私有方法和访问器的例子:

class Player {
  #currentSong;

  constructor() {
    // 初始化播放器
  }

  #playSong() {
    // 播放当前歌曲
  }

  play() {
    this.#playSong();
  }

  #pauseSong() {
    // 暂停当前歌曲
  }

  pause() {
    this.#pauseSong();
  }
}

const player = new Player();
player.play();

通过使用私有方法和访问器,我可以将播放器类的内部实现细节隐藏起来,只暴露出必要的公共接口。这样,我可以更好地封装和保护类的内部逻辑,提高了代码的安全性和可维护性。

Promise.allSettled()

我们经常需要处理多个 Promise 对象,并在它们全部解决或拒绝后进行相应的操作。在 ES2021 中,引入了 Promise.allSettled() 方法,它为我们提供了一种更灵活和全面的处理多个 Promise 的方式。

有一次,我正在开发一个电商网站,其中一个功能是展示多个供应商的商品信息。我需要同时向多个供应商的 API 发送请求,并在所有请求完成后,获取每个供应商的商品信息。

在过去,我通常会使用 Promise.all() 方法来等待所有请求完成,并在所有请求成功后获取结果。然而,这种方式有一个缺点,即一旦其中一个请求失败,整个链路就会被拒绝,导致无法获取其他请求的结果。

幸运的是,在 ES2021 中引入了 Promise.allSettled() 方法,它允许我们等待多个 Promise 的状态全部被解决,并返回一个包含每个 Promise 结果的数组,无论是成功还是失败。这为我解决了之前遇到的问题。

让我们来看一下我是如何使用 Promise.allSettled() 方法来获取多个供应商的商品信息的例子:

const fetchProductInfo = async () => {
  const promises = [
    fetchProductInfoFromSupplier1(),
    fetchProductInfoFromSupplier2(),
    fetchProductInfoFromSupplier3()
  ];

  const results = await Promise.allSettled(promises);

  const productInfo = results.map(result => {
    if (result.status === 'fulfilled') {
      return result.value;
    } else {
      return { error: result.reason };
    }
  });

  console.log(productInfo);
};

fetchProductInfo();

通过使用 Promise.allSettled() 方法,我可以并行地发起多个请求,并在所有请求完成后获取每个请求的结果。无论请求是成功还是失败,我都能够获取到对应的结果,而不会因为其中一个请求的失败而导致整个链路被拒绝。

在这个故事中,Promise.allSettled() 方法为我提供了一种更灵活、更全面的处理多个 Promise 的方式。我感到非常激动和满意,因为它帮助我更好地处理异步操作,提升了代码的健壮性和可靠性。

Well-formed JSON.stringify()

我们经常需要将 JavaScript 对象转换为 JSON 字符串,以便在网络传输或存储时进行数据交换。在 ES2021 中,引入了 Well-formed JSON.stringify() 特性,它为我们提供了一种更可靠的方式来序列化 JavaScript 对象。

有一次,我正在开发一个社交媒体应用,用户可以发布帖子并分享到其他平台。为了实现这个功能,我需要将帖子对象转换为 JSON 字符串,并将其发送到其他平台的 API。

在过去,我通常会使用 JSON.stringify() 方法来将对象转换为 JSON 字符串。然而,这种方式存在一些问题,特别是在处理特殊字符时。有时候,JSON.stringify() 方法会生成一些不符合规范的 JSON 字符串,导致在其他平台上解析失败。

幸运的是,在 ES2021 中引入了 Well-formed JSON.stringify() 特性,它确保了生成的 JSON 字符串始终符合规范。这让我可以更加放心地将 JavaScript 对象转换为 JSON 字符串,并进行数据交换。

让我们来看一下我是如何使用 Well-formed JSON.stringify() 特性将帖子对象转换为 JSON 字符串的例子:

const post = {
  id: 1,
  title: 'Hello, world!',
  content: 'This is a sample post.'
};

const jsonString = JSON.stringify(post, null, 2);

console.log(jsonString);

通过使用 Well-formed JSON.stringify() 特性,我可以生成一份符合规范的 JSON 字符串,其中包括缩进和换行。这让我能够更好地阅读和调试生成的 JSON 字符串,同时确保了在其他平台上的解析成功。

AggregateError

我们经常需要处理多个 Promise 的错误。在 ES2021 中,引入了 AggregateError 类型,它为我们提供了一种更好地处理多个错误的方式。

有一次,我正在开发一个电商网站,其中一个功能是展示多个供应商的商品信息。我需要同时向多个供应商的 API 发送请求,并在有错误发生时进行相应的处理。

在过去,我通常会使用 Promise.all() 方法来等待所有请求完成,并在有错误发生时抛出一个通用的错误。然而,这种方式无法提供更详细的错误信息,也无法区分不同的错误来源。

幸运的是,在 ES2021 中引入了 AggregateError 类型,它允许我们创建一个聚合错误对象,包含了多个错误的详细信息。这为我解决了之前遇到的问题。

让我们来看一下我是如何使用 AggregateError 类型来处理多个供应商请求的错误的例子:

const fetchProductInfo = async () => {
  const promises = [
    fetchProductInfoFromSupplier1(),
    fetchProductInfoFromSupplier2(),
    fetchProductInfoFromSupplier3()
  ];

  try {
    await Promise.all(promises);
  } catch (error) {
    if (error instanceof AggregateError) {
      for (const subError of error) {
        console.error(subError);
      }
    } else {
      console.error(error);
    }
  }
};

fetchProductInfo();

通过使用 AggregateError 类型,我可以捕获到多个请求的错误,并逐个处理每个错误。这样我就能够提供更详细的错误信息,区分不同的错误来源,并进行相应的处理。

最后:希望这些内容能够帮助你更好地理解和应用这些特性。如果你还有其他问题,欢迎继续提问!🎉

注:各版本存在重复特性可能是因为这些特性在当前版本中被初步提出,但由于一些原因未能及时成为标准的一部分。因此,在随后中,为了确保这些特性的稳定性和可用性,它们被再次列入标准。