ES9(ECMAScript 2018)为JavaScript语言带来了多项新特性,这些特性旨在简化异步编程、对象操作等常见任务。主要包含:异步迭代器、 Promise.finally()、Rest/Spread 属性、正则表达式命名捕获组、正则表达式反向断言 正则表达式Unicode属性转义、正则表达式dotAll模式、模板字符串修订共八个新特性。
一、异步迭代器(Asynchronous Iterators)
特性说明
异步迭代器Async iterator
对象的 next() 方法返回一个 Promise
,这个 Promise
的返回值可以被解析成 {value, done}
的格式。
异步迭代器允许我们使用for-await-of
循环异步迭代数据,这对于处理异步数据源(如文件流、网络请求等)非常有用。
优势
- 代码简洁:简化了异步操作的编写,使得异步代码更易于阅读和维护。
- 流程控制:可以像同步迭代器一样控制异步操作的流程。
使用方法
async function* readLines(path) {
let file = await fileOpen(path);
try {
while (!file.EOF) {
yield await file.readLine();
}
} finally {
await file.close();
}
}
for await (const line of readLines(filePath)) {
console.log(line);
}
注意事项
- 兼容性:并非所有环境都支持异步迭代器,需检查目标环境是否支持。
- 错误处理:异步操作可能会抛出异常,应当使用
try...catch
进行错误处理。
二、Promise.finally()
特性说明
Promise.finally()
方法在Promise执行完毕后,无论结果是fulfilled还是rejected,都会执行的一个回调函数。
优势
- 统一资源管理:无需关心Promise的状态,即可在操作完成后进行资源清理。
- 代码整洁:避免在
then
和catch
中重复相同的代码。
使用方法
fetch(url)
.then(response => response.json())
.catch(error => console.error('Error:', error))
.finally(() => console.log('Fetch complete'));
注意事项
- 执行时机:
finally
回调不接收任何参数,无法得知Promise的状态。 - 异步执行:
finally
中的回调是异步执行的。
三、Rest/Spread 属性
特性说明
Rest属性允许我们将对象剩余的属性收集到一个新的对象中。Spread属性则允许我们将一个对象展开到另一个对象中。
优势
- 灵活性:可以轻松创建对象的副本,或在创建新对象时包含现有对象的属性。
- 简洁性:减少了重复编写代码的需要。
使用方法
// Rest属性
const { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(z); // { a: 3, b: 4 }
// Spread属性
const n = { ...z, x: 5 };
console.log(n); // { a: 3, b: 4, x: 5 }
ES5实现方法
// Rest属性
var obj = { x: 1, y: 2, a: 3, b: 4 };
var x = obj.x;
var y = obj.y;
var z = {};
for (var key in obj) {
if (key !== 'x' && key !== 'y') {
z[key] = obj[key];
}
}
// Spread属性
var n = {};
for (var key in z) {
n[key] = z[key];
}
n.x = 5;
注意事项
- 浅拷贝:Spread操作是浅拷贝,不会复制对象的原引用类型的深层次结构。
- 原型链:Spread操作不会包含对象的原型链上的属性。
四、正则表达式命名捕获组(Named Capture Groups)
特性说明
ES9允许在正则表达式中使用命名捕获组,这使得匹配的结果可以通过名称而不是数字索引来引用。
优势
- 可读性:通过名称引用捕获组比数字索引更直观,特别是在复杂的正则表达式中。
- 易于维护:当正则表达式结构发生变化时,只需要修改名称,而不必更新索引。
使用方法
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec('2023-04-01');
console.log(match.groups.year); // 2023
console.log(match.groups.month); // 04
console.log(match.groups.day); // 01
ES5实现方法
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var match = regex.exec('2023-04-01');
var groups = {
year: match[1],
month: match[2],
day: match[3]
};
console.log(groups.year); // 2023
console.log(groups.month); // 04
console.log(groups.day); // 01
注意事项
- 兼容性:不是所有的JavaScript环境都支持命名捕获组,使用前需要检查。
- 性能:在某些情况下,使用命名捕获组可能会稍微影响正则表达式的性能。
五、正则表达式反向(后顾)断言(Lookbehind Assertions)
特性说明
ES9引入了反向断言,允许正则表达式匹配字符串中的某个模式之前或之后的文本。/(?<=exp2)exp1/
前瞻断言 /exp1/(?=exp2)
优势
- 灵活性:可以更精确地控制匹配的模式,特别是在需要依赖上下文的情况下。
- 简洁性:避免了复杂的正则表达式结构。
使用方法
const regex = /(?<=\$)\d+/;
const match = regex.exec('$100');
console.log(match[0]); // 100
ES5实现方法
由于ES5不支持反向断言,通常需要通过字符串操作来模拟,这可能会非常复杂且效率低下。
注意事项
- 贪婪顺序:贪婪匹配顺序从右到左
/(?<=(\d+)(\d+))$/.exec('1053')
['', '1', '053']
- 反向引用顺序:断言内部反向引用使用时匹配组必须在右侧
/(?<=(.)\1)\d/.exec('a2bb3')
和/(?<=\1(.))\d/.exec('a2bb3')
两个重复的值后边的数字。 - 性能:复杂的正则表达式可能会影响执行效率。
六、 RegExp Unicode Property Escapes(正则表达式Unicode属性转义)
说明
正则表达式Unicode属性转义是ES9引入的一个新特性,它允许在正则表达式中使用\p{...}
和\P{...}
转义序列来匹配符合Unicode属性的字符。这为正则表达式提供了对Unicode字符集的更细粒度的控制。
优势
- 强大的匹配能力:允许你精确匹配Unicode字符的属性,如字母、数字、标点符号等。
- 代码简洁性:减少了使用复杂的字符类和Unicode范围来匹配特定属性字符的需要。
使用方法
const regex = /\p{Lowercase}/u; // 匹配任何小写字母
const regex2 = /\P{Lowercase}/u; // 匹配任何非小写字母
const match = regex.exec('a'); // 匹配成功,返回'a'
const match2 = regex2.exec('A'); // 匹配成功,返回'A'
在上述例子中,\p{Lowercase}
匹配任何小写字母,而\P{Lowercase}
匹配任何非小写字母。u
标志是必须的,它指示正则表达式引擎处理Unicode属性转义。
ES5实现方法
在ES5中,没有直接的语法支持Unicode属性转义。你需要使用字符类和Unicode范围来模拟相同的功能。
const regex = /[a-z]/; // 匹配任何小写字母
const regex2 = /[^a-z]/; // 匹配任何非小写字母
const match = regex.exec('a'); // 匹配成功,返回'a'
const match2 = regex2.exec('A'); // 匹配成功,返回'A'
注意事项
- 兼容性:Unicode属性转义是ES9引入的特性,不是所有JavaScript环境都支持。
- 性能:使用Unicode属性转义可能会稍微影响正则表达式的性能。
- 准确性:Unicode属性转义提供了一种精确匹配Unicode字符属性的方法,但需要注意某些属性可能会有多个字符匹配。
七、 正则表达式dotAll模式(s标志)
说明
在正则表达式中,点号(.
)通常用于匹配除换行符外的任意单个字符。ES9引入了s
标志(也称为dotAll模式),它允许点号匹配任意字符,包括换行符。这意味着在点号前加上s
标志后,点号将不再跳过换行符。
优势
- 匹配任意字符:使得点号在正则表达式中可以匹配任意字符,包括换行符。
- 简化正则表达式:无需使用多个字符类来匹配任意字符,包括换行符。
使用方法
const regex = /foo.bar/s; // 匹配'foo\nbar'
const match = regex.exec('foo\nbar'); // 匹配成功,返回'foo\nbar'
在上述例子中,/foo.bar/s
将匹配字符串'foo\nbar'
中的'foo\nbar',包括换行符。
ES5实现方法
在ES5中,没有直接的语法支持s
标志。你需要使用多个字符类或特定的Unicode范围来匹配任意字符,包括换行符。
const regex = /foo[\s\S]bar/; // 匹配'foo\nbar'
const match = regex.exec('foo\nbar'); // 匹配成功,返回'foo\nbar'
八、 Template Literal Revision(模板字符串修订)
说明
模板字符串修订是ES9引入的一个小特性,放松了对标签函数里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回 undefined
,而不是报错,并且从 raw
属性上面可以得到原始字符串。
之前的问题:
function latex(strings) {
// ...
}
let document = latex`
\newcommand{\fun}{\textbf{Fun!}} // 正常工作
\newcommand{\unicode}{\textbf{Unicode!}} // 报错
\newcommand{\xerxes}{\textbf{King!}} // 报错
Breve over the h goes \u{h}ere // 报错
`
上面代码中,变量 document
内嵌的模板字符串,对于 LaTEX 语言来说完全是合法的,但是 JavaScript 引擎会报错。原因就在于字符串的转义。
模板字符串会将\u00FF
和\u{42}
当作 Unicode 字符进行转义,所以\unicode
解析时报错;而\x56
会被当作十六进制字符串转义,所以\xerxes
会报错。也就是说,\u
和\x
在 LaTEX 里面有特殊含义,但是 JavaScript 将它们转义了。
优势
- 增强适用性:解决LaTEX里面特殊含义的字符和模板字符串转义字符的冲突.
使用方法
function tag(strs) {
strs[0] === undefined
strs.raw[0] === '\unicode and \u{55}'
}
tag`\unicode and \u{55}`
在这个例子中,tag
函数的参数strings
是一个包含原始模板字符串的数组,values
是一个包含要插入到模板字符串中的值的数组。strings.raw[0]
和strings.raw[1]
分别代表模板字符串的第一行和第二行。
注意事项
- 兼容性:这种对字符串转义的放松,只在标签模板解析字符串时生效,不是标签模板的场合,依然会报错。。
let bad = `bad escape sequence: \unicode` // 报错