跳到主要内容

ES15 (ES2024)

· 阅读需 9 分钟

ES2024(ES15)引入了几个重要的新特性,包括强大的分组功能、Promise 控制增强以及正则表达式的改进。这些更新进一步提升了 JavaScript 的开发体验和功能。

ES2024(ES15)新特性如下:

  1. GroupBy分组
  2. Promise.withResolvers
  3. 正则表达式的 /v 标志

1. GroupBy分组

GroupBy 是 ES2024 引入的新特性,它们为开发者提供了一种强大的分组功能,允许根据特定的回调函数对数组或对象中的元素进行分组。该新特性在分析统计数据时非常有用,比如将用户按年龄段分组、按销售额分组等,可以轻松实现。分组对应的方法有两个:

  1. Object.groupBy( ):返回一个对象
  2. Map.groupBy( ):返回一个 Map

Object.groupBy( ) 语法如下:

Object.groupBy(array, callback);
  • array:需要分组的数组
  • callback:一个回调函数,用来确定分组依据。它会接收当前数组的每一个元素,返回用于分组的键值。
const fruits = [
{ name: "apple", type: "pome" },
{ name: "banana", type: "berry" },
{ name: "cherry", type: "drupe" },
{ name: "pear", type: "pome" },
];

const groupedFruits = Object.groupBy(fruits, (f) => f.type);
console.log(groupedFruits);
const fruits = [
{ name: "pineapple🍍", color: "🟡" },
{ name: "apple🍎", color: "🔴" },
{ name: "banana🍌", color: "🟡" },
{ name: "strawberry🍓", color: "🔴" },
];
const groupedFruits = Object.groupBy(fruits, (f) => f.color);
console.log(groupedFruits);

Map.groupBy( ) 用法和上面的 Object.groupBy 基本相似,不过返回的是一个 Map. 对应的语法如下:

Map.groupBy(array, callback);
  • array:要分组的数组
  • callback:一个回调函数,返回的值作为 Map 的键
const arr = [6.1, 4.2, 6.3];
// 按照向下取整后进行分组
const grouped = Map.groupBy(arr, Math.floor);
console.log(grouped);
const grouped2 = Object.groupBy(arr, Math.floor);
console.log(grouped2);

实际上这两个方法是 Lodash 里面使用频率比较高的方法,随着 ES 版本的更新,越来越多以前需要靠第三方库支持的特性,直接原生开始支持了。

2. Promise.withResolvers

Promise.withResolvers 是 ECMAScript 提案中新增的一个静态方法,旨在简化创建 Promise 及其关联的 resolve 和 reject 函数的过程。通过这个方法,开发者可以更方便地创建手动控制的 Promise 对象,而无需像传统方式那样使用额外的变量。

先回顾一下传统的方式。

new Promise((resolve, reject)=>{
// 异步处理
// ...
resolve(...);
})

在使用 Promise 的时候,经常会有这样的需求:需要手动控制 Promise 何时进行 resolve 或 reject,而非在异步操作完成后自动 resolve 或 reject,这种需求下通常创建一个"延迟对象"(Deferred Object),以便在异步操作完成后再决定何时以及如何处理 Promise.

class Deferred{
constructor(){
this.promise = new Promise((resolve, reject)=>{
this.reoslve = resolve;
this.reject = reject;
})
}
}
const d = new Deferred();
// 后期就可以手动控制何时进行 resolve
d.reoslve();

举一个实际的场景:假设有一个需要向多个不同的第三方 API 发送请求的应用程序。每个请求的响应时间和成功率都不同。我们需要在所有请求都成功后,才能进行下一步操作。如果任何一个请求失败,都需要立即停止整个流程,并进行错误处理。

const apiEndpoints = [
"https://api.example.com/data1",
"https://api.example.com/data2",
"https://api.example.com/data3",
];

/**
*
* @param {*} apiEndpoints 数组,存放了多个API的地址
*/
function fetchMutipleAPIs(apiEndpoints) {
let resolve, reject; // 保存resolve和reject函数

const p = new Promise((res, rej) => {
// 将两个方法提取出来,回头可以手动决定什么时候resolve和reject
resolve = res;
reject = rej;
});

let completed = 0; // 记录已经完成的请求数量
const total = apiEndpoints.length; // 总请求数量
const results = []; // 存放结果

apiEndpoints.forEach((endPoint, index) => {
fetchDataFromURL(endPoint)
.then((data) => {
results[index] = data;
completed++;
if (completed === total) {
// 进入该分支,说明所有请求都已经完成
// 然后再手动的resolve整体的结果
resolve(results);
}
})
.catch((e) => {
console.log("请求失败error:", e.message);
});
});

return p;
}

function fetchDataFromURL(url) {
return new Promise((resolve, reject) => {
const delay = Math.random() * 2000;
const isSuccessful = Math.random() > 0.2; // 80%的概率请求成功
setTimeout(() => {
if (isSuccessful) {
resolve(`Data from ${url}`);
} else {
reject(new Error(`请求 ${url} 失败`));
}
}, delay);
});
}

fetchMutipleAPIs(apiEndpoints)
.then((result) => {
console.log("所有请求都已经完成,结果为:", result);
})
.catch((err) => {
console.log("请求失败", err);
});

那么现在,我们不需要再在 Promise 构造函数的执行器函数中手动将 resolve 和 reject 赋值给外部变量。这种方式较为繁琐,且代码不够简洁。直接使用 Promise.withResolvers,可以在一行代码中同时获取 promise、resolve 和 reject,使代码更简洁明了。

语法如下:

const { promise, resolve, reject } = Promise.withResolvers();
  • promise:一个新的 Promise 实例
  • resolve:与该 Promise 实例关联的 resolve 函数
  • reject:与该 Promise 实例关联的 reject 函数

我们使用 Promise.withResolvers( ) 来重构上面的场景示例代码:

function fetchMutipleAPIs(apiEndpoints) {
const { promise, resolve, reject } = Promise.withResolvers();

// ...

return promise;
}

3. 正则表达式的 /v 标志

/v 标志代表 Unicode Sets 模式(Unicode 属性集模式)。引入这个标志的目的是为了提供一种更强大、更直观的方式来处理 Unicode 字符集,尤其是在需要匹配复杂的 Unicode 属性组合时。

当使用 v 标志时,正则表达式引擎将采用新的解析方式,可以使用以下新的语法特性:

  1. 字符集操作:支持集合的 并集([...||...])、交集([...&&...])和 差集([...--...])

    • 使用 || 表示两个字符集的并集

      // 匹配拉丁文或者希腊字母(目前最新的 node.js 环境还不支持)
      const regex = /[\p{Script=Latin}||\p{Script=Cyrillic}]/v;
      console.log(regex.test("A")); // 输出:true
      console.log(regex.test("Б")); // 输出:true
      console.log(regex.test("汉")); // 输出:false
    • 使用 && 表示两个字符集的交集

      const regex = /[\p{Alphabetic}&&\p{Script=Latin}]/v;
      console.log(regex.test("A")); // 输出:true
      console.log(regex.test("1")); // 输出:false
    • 使用 -- 表示字符集的差集

      // 匹配拉丁文,但不包括小写的字母
      const regex = /[\p{Script=Latin}--\p{Lowercase_Letter}]/v;
      console.log(regex.test("A")); // 输出:true
      console.log(regex.test("a")); // 输出:false

      // 匹配所有数字,但不包括罗马数字(目前最新的 node.js 环境还不支持)
      const regex = /[\p{Number}--\p{Numeric_Type=Numeric}]/v;
      console.log(regex.test("5")); // 输出:true
      console.log(regex.test("Ⅳ")); // 输出:false
  2. 嵌套的字符类:可以在字符类内部使用字符类

    const regex = /[[a-f]&&[d-z]]/v;
    console.log(regex.test("e")); // 输出:true
    console.log(regex.test("b")); // 输出:false

最后要说的是,/v 标志目前仍是提案阶段,需确认运行环境是否支持。

ES2024 其他新特性:

  • 可调整大小的 ArrayBuffer
  • Atomics.waitAsync( ) 方法
  • 字符串 isWellFormed( ) 和 toWellFormed( ) 方法

-EOF-