跳到主要内容

ES14 (ES2023)

· 阅读需 8 分钟

ES2023(ES14)引入了一系列实用的新特性,包括从右向左查找数组元素的方法、Hashbang语法支持、不修改原数组的数组操作方法、Set集合的增强功能以及允许Symbol作为WeakMap键。这些更新使JavaScript代码在处理数组和集合时更加灵活,同时提供了更好的脚本执行和内存管理能力。

ES2023(ES14)带来的新特性有:

  1. findLast( ) 和 findLastIndex( )
  2. Hashbang 语法
  3. Change Array by Copy Methods
  4. Set 方法的增强
  5. Symbols as WeakMap keys

1. findLast( ) 和 findLastIndex( )

findLast( ) 和 findLastIndex( ) 为数组提供了一种从右到左查找元素的方法,类似于 find( ) 和 findIndex( ),但从数组的末尾开始查找。

const arr = [11, 24, 13, 74, 55];

console.log(arr.find((x) => x % 2 === 0));
console.log(arr.findIndex((x) => x % 2 === 0));


console.log(arr.findLast((x) => x % 2 === 0));
console.log(arr.findLastIndex((x) => x % 2 === 0));

2. Hashbang 语法

Hashbang Grammar 是一个常见的 Unix 特性,用于告诉系统应该使用哪个解释器来运行脚本文件

在 Unix 和 Linux 环境中,hashbang (#!) 是脚本文件的第一行,通常用于指定该脚本应由哪个解释器运行。这样,脚本文件可以直接作为可执行文件运行,而不必手动调用解释器。

例如,传统的 Unix 脚本文件的开头可能是:

#!/usr/bin/env bash

这表示使用 /usr/bin/env 运行 bash 解释器来执行该脚本。

ES2023 引入了 Hashbang Grammar 特性,允许 JS 文件在文件的第一行包含 hashbang(#!),从而使 JS 文件可以在类似 Unix 的环境中直接执行,而不需要明确地调用解释器(如 node)

#!/usr/bin/env node
console.log('Hello, World!');

在这个例子中,#!/usr/bin/env node 指定了使用 node 作为解释器。当你在 Unix/Linux 系统中执行这个文件时,系统会自动使用 node 来运行它,而不需要手动输入 node filename.js

注意点1:如果直接执行,可能会报错:zsh: permission denied: ./index.js

这表示文件没有执行权限。解决方案也很简单,在终端中使用 chmod 命令为文件添加执行权限。运行以下命令:

chmod +x ./index.js

在确保文件有可执行权限后,运行以下命令来执行 index.js:

./index.js

注意点2:在 Windows 中的限制

hashbang 语法通常在 Unix 和 Linux 环境下工作,而不是直接在 Windows 环境下。

对于 Windows,通常需要手动调用解释器(例如,使用 node index.js)

实战应用案例

开发一个简单的命令行工具,具有以下功能:

  • 添加任务:将新的任务添加到任务列表中

    ./todo.js add "完成项目文档"
    ./todo.js add "部署应用到生产环境"
  • 查看任务:列出所有待办任务

    ./todo.js list
  • 标记任务为已完成:将某个任务标记为已完成

    ./todo.js complete 1
  • 删除任务:从任务列表中删除已完成的任务

    ./todo.js clear

3. Change Array by Copy Methods

新增了一些数组方法,这些方法在不修改原数组的情况下返回操作后的新数组。这些方法包括:

  • toReversed( ):返回一个反转后的新数组
  • toSorted( ):返回排序后的新数组(基于可选的比较函数)
  • toSpliced( ):返回移除或替换部分元素的新数组(基于 splice 操作)
  • with( ):返回一个替换指定索引的新数组
const arr = [1, 2, 3, 4, 5];

console.log(arr.toReversed());
console.log(arr);

console.log(arr.toSorted((a, b) => b - a));
console.log(arr);

console.log(arr.toSpliced(1, 3)); // 包含了结束下标
console.log(arr);

console.log(arr.with(0, 100));
console.log(arr);

4. Set 方法的增强

ES2023 新增了一些适用于 Set 数据结构的方法,这些方法主要是和集合操作相关的。包括:

  • Set.prototype.intersection( ):返回两个集合的交集
  • Set.prototype.union( ):返回两个集合的并集
  • Set.prototype.difference():返回两个集合的差集
  • Set.prototype.symmetricDifference():返回两个集合的对称差集
  • Set.prototype.isDisjointFrom():判断两个集合是否不相交
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);

console.log(setA.intersection(setB));
console.log(setA.union(setB));
console.log(setA.difference(setB)); // 差集,返回在 A 中但不在 B 中的元素
console.log(setA.symmetricDifference(setB)); // 对称差集,返回在 A 或 B 中,但不会同时出现在 A 和 B 中的元素
console.log(setA.isDisjointFrom(setB)); // 是否互斥,返回 A 和 B 是否没有交集

5. Symbols as WeakMap keys

在 ES2023 之前,WeakMap 和 WeakSet 的键只能是对象,而不能是 Symbol。这种限制的原因是 Symbol 不像对象那样容易被垃圾回收,因此无法作为弱引用的键。为了扩展 JS 的弱引用集合的功能,ES2023 引入了这一特性,未注册的 Symbol 可以作为 WeakMap 和 WeakSet 的键。

所谓未注册Symbol指的就是不是使用 Symbol.for 生成Symbol

示例 1:在 WeakMap 中使用 Symbol 作为键

let s = Symbol("foo"); // 未注册的 Symbol
let obj = {
name: "bar",
};
let wm = new WeakMap();
// 在ES2023之前,weakmap的键只能是对象,不能是Symbol
// 但是在ES2023中,WeakMap的键可以是Symbol
wm.set(s, obj);
console.log(wm.get(s)); // { name: 'bar' }

示例 2:在 WeakSet 中使用 Symbol

let s = Symbol("foo"); // 未注册的 Symbol
let ws = new WeakSet();

ws.add(s);
console.log(ws.has(s)); // true

示例 3:WeakRef 和 FinalizationRegistry 支持 Symbol

let sym = Symbol("foo");
let wr = new WeakRef(sym);

console.log(wr.deref()); // 输出: Symbol(foo)

let fr = new FinalizationRegistry((value) => {
console.log("Finalized:", value);
});
let obj = { name: "bar" };
fr.register(obj, "bar", sym);
fr.unregister(sym);

在这个例子中,WeakRef 也支持 Symbol。此外,FinalizationRegistry 也可以使用 Symbol 进行注册和取消注册。

尽管 Symbol 现在可以作为 WeakMap 和 WeakSet 的键,但注册的 Symbol(通过 Symbol.for() 注册的符号)仍然不能作为键。这是因为注册的符号全局共享,并且不会被垃圾回收,因此不适合作为弱引用的键。

let s = Symbol.for("foo"); // 注册的 Symbol
let obj = {
name: "bar",
};
let wm = new WeakMap();
// 注册的Symbol不能作为WeakMap的键
wm.set(s, obj);
// TypeError: Invalid value used as weak map key

-EOF-