跳到主要内容

在 2018 年 6 月正式发布 ECMAScript 2018(简称 ES9),进一步完善了语言特性。主要聚焦于异步操作、对象操作、正则表达式增强和 Promise 扩展等。

一、 异步迭代(async iteration)

信息

es6 引入了迭代器和 for...of,但是对异步数据的迭代支持不够好

新增异步迭代器(async iterator)异步可迭代对象(async iterable) ,并提供了 for-await-of 循环用于遍历异步数据。

  • 异步迭代器:类似于同步迭代器,但 next() 方法返回一个 Promise ,该 Promise resolved 结果为 {value, done}
  • 异步可迭代对象:对象实现了 [Symbol.asyncIterator]() 方法,该方法返回异步迭代器
  • for-await-of:专门用于遍历异步可迭代对象,自动处理每一个迭代步骤的 Promise
// 异步可迭代对象(模拟异步获取数据)
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
next() {
if (i < 3) {
// 返回Promise,模拟异步操作
return Promise.resolve({ value: i++, done: false });
}
return Promise.resolve({ done: true });
},
};
},
};

// 使用for-await-of遍历
(async () => {
for await (const num of asyncIterable) {
console.log(num); // 依次输出:0, 1, 2
}
})();
  • 应用场景:处理异步生成的数据流(如分页加载数据、实时日志流),简化异步循环逻辑

二、 对象的 Rest/Spared 属性(Rest/Spread Properties)

信息

se6 引入了数组的 Rest (剩余)和 Spread (扩展)运算符,但是未支持对象

将 Rest/Spread 扩展到对象,用于方便的复制、合并对象后提取部分属性

  • 对象 Spared(...):将一个对象的所有可枚举属性复制到另一个对象,可用于对象复制或合并
  • 对象 Rest(...):在对象结构中,将未被显示结构的剩余属性收集到一个新对象中
注意
  • Spread 仅复制对象的自身可枚举属性,不包含继承属性或 Symbol 属性(需显式处理)
  • 若源对象右 getterSpread 会执行 getter 并复制结果,而不是复制 getter 本身

三、 正则表达式增强

1、 命名捕获组(named capture groups)

信息

传统的捕获组使用数字索引(如 $1$2 )访问,可读性差且易因分组顺序变化而导致错误

允许为捕获组命名,通过命名访问匹配结果,增强代码的可维护性。

使用 (?<命名>捕获组) 的方式为捕获组命名

// 匹配日期(年-月-日)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const result = dateRegex.exec('2023-10-05');

// { year: '2023', month: '10', day: '05' }
console.log(result.groups);

// '2023'
console.log(result.groups.year);

// 替换时也可使用命名引用:$<name>
// '10/05/2023'
console.log('2023-10-05'.replace(dateRegex, '$<month>/$<day>/$<year>'));

2、 反向断言(lookbehind assertions)

信息

ES5 支持前瞻断言(lookahead) (判断后面是不是有特定的内容),但不支持后顾断言(lookbehind)(判断前面是否有特定内容)

新增两种反向断言方式:

  • 正向后顾 ((?<=匹配组)):匹配前面是否是 匹配组 的内容
  • 负向后顾 ((?<!匹配组>)):匹配前面是不是 匹配组 的内容
// 正向后顾:匹配前面是$的数字
const priceRegex = /(?<=\$)\d+/;
console.log(priceRegex.exec('$100')); // 匹配'100'
console.log(priceRegex.exec('€100')); // null(前面不是$)

// 负向后顾:匹配前面不是$的数字
const notPriceRegex = /(?<!\$)\d+/;
console.log(notPriceRegex.exec('€100')); // 匹配'100'
console.log(notPriceRegex.exec('$100')); // null(前面是$)

3、 Unicode 属性转义 (Unicode property escapes)

信息

传统的正则难以匹配多语言字符(如中文、希腊字母、emoji)

允许通过 Unicode 属性(如脚本类型、数字、字母等)匹配字符,语法为 \p{Property=Value} (匹配符合属性的字符)或 \P{Property=Value} (匹配不符合属性的字符)

// 匹配所有希腊字母(Script=Greek)
const greekRegex = /\p{Script=Greek}/u; // 需加u标志启用Unicode模式
console.log(greekRegex.test('α')); // true(希腊字母α)
console.log(greekRegex.test('a')); // false(拉丁字母)

// 匹配所有数字(包括中文数字、罗马数字等)
const numberRegex = /\p{Number}/u;
console.log(numberRegex.test('123')); // true
console.log(numberRegex.test('一二三')); // true(中文数字)
console.log(numberRegex.test('Ⅷ')); // true(罗马数字8)

4、 ditAll 模式 (s 标志)

信息

正则中的 . 默认匹配除了换行符 (\n\r 等)外的任意字符,如需匹配换行符需要手动处理 (如 [\s\S])

添加了 s 标志符用于增强正则表达式对 . 的匹配规则,现可匹配到包括换行符在内的任意字符

const str = 'hello\nworld';

// 无s标志:.不匹配换行
console.log(/hello.world/.test(str)); // false

// 有s标志:.匹配换行
console.log(/hello.world/s.test(str)); // true

四、 Promise.prototype.finally()

添加 finally 方法用于指定的 promise 无论是在 resolved 还是在 rejected 都会执行的回调,避免代码的重复。

// 传统Promise链的清理操作(需重复编写)
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log(data);
hideLoading(); // 清理操作
})
.catch(error => {
console.error(error);
hideLoading(); // 清理操作
});

// 使用finally()简化
fetch('/api/data')
.then(response => response.json())
.catch(error => console.error(error))
.finally(() => hideLoading()); // 无论成功/失败,都执行hideLoading()

五、模版字符串限制解除

ES2018 接触了模版字符串中转义序列的严格检查,允许使用 \u\x 等转义序列,即使他们不符合 Unicode 或十六进制格式(如 \unicode 不会抛出语法错误,而是视为普通字符串)

// ES2018之前(语法错误:无效的Unicode转义序列)
// const str = `\unicode`;

// ES2018之后(视为普通字符)
const str = `\unicode`;
console.log(str); // '\unicode'

六、 可选 catch 绑定

ES2018 允许在 catch 中省略(即不定义错误变量),适用于不需要处理错误信息的场景。

// ES2018之前(需定义error变量,即使不使用)
try {
JSON.parse('invalid json');
} catch (error) {
console.log('解析失败');
}

// ES2018之后(省略error变量)
try {
JSON.parse('invalid json');
} catch {
console.log('解析失败');
}

七、 JSON 超集

ES2018 允许了 JSON 中出现包含未转义的行终止符(如 \n\r\u2028(行分隔符)、\u2029(段分隔符)),解决了传统 JSON 中“行终止符需转义”的问题。

// ES2018 之前(语法错误:未转义的行分隔符)
// const json = '{"text": "hello\u"}'

// ES2018 之后,未经转的行分隔符
const json = '{"text": "Hello\u2028World"}';
const obj = JSON.parse(json);

console.log(obj.text); // 'Hello\u2028World'