ECMAScript日常总结–ES2018(ES9)
文章目录
1. for-await-of – 异步迭代
异步迭代允许你通过 for-await-of
循环迭代异步产生的数据,如从异步生成器函数中产生的数据。for-await-of
主要用于遍历实现了异步迭代器的对象,以及在异步上下文中处理异步操作的场景。
语法
for-await-of
的语法结构如下:
for await (variable of asyncIterable) {
// 异步操作
}
variable
: 用于接收异步迭代器返回值的变量。asyncIterable
: 实现了Symbol.asyncIterator
方法的异步可迭代对象。
async function* asyncGenerator() {
for (let i = 0; i < 3; i++) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作
yield i;
}
}
async function iterateAsyncGenerator() {
for await (const value of asyncGenerator()) {
console.log(value); // 每隔一秒输出一个值
}
}
iterateAsyncGenerator();
asyncGenerator
是一个异步生成器,它每秒产生一个值。通过 for await (const value of asyncGenerator())
,可以清晰地遍历异步生成器的值,并在每次值可用时执行相应的异步操作。
使用场景
- 异步生成器和异步可迭代对象: 主要用于遍历实现了异步迭代器的对象,包括异步生成器和实现了
Symbol.asyncIterator
方法的异步可迭代对象。 - 处理异步操作: 适用于需要等待异步操作完成后再进行下一步的情况。通过
for-await-of
可以更清晰地编写异步代码,而不必手动管理 Promise 的解析和处理。
注意
异步上下文: 使用
for-await-of
的代码必须在异步函数内,因为它自身需要在异步上下文中执行。等待每个值:
for-await-of
会自动等待每个异步迭代器的next
方法返回的 Promise 解析之后再进行下一步。这使得代码更直观,不需要手动处理 Promise 解析。处理错误: 如果异步迭代器中的 Promise 被拒绝,
for-await-of
会在循环体内捕获并抛出错误。因此,在使用时要考虑适当的错误处理机制。
总的来说,for-await-of
是一个使得处理异步迭代变得更加方便和清晰的语法糖,特别适用于异步代码中需要遍历异步生成器或异步可迭代对象的情况。
2. Promise.finally() – Promise结束时无论是完成还是拒绝都会执行的回调函数
Promise.finally()
用于注册一个在 Promise
结束时无论是完成还是拒绝都会执行的回调函数。finally
方法的主要目的是为了在 Promise
执行结束后执行清理工作,无论 Promise 的状态是完成还是拒绝。
语法
promise.finally(onFinally);
promise
:一个Promise
对象。onFinally
:在Promise
结束时(不论是完成还是拒绝)执行的回调函数。
finally
方法返回一个新的 Promise
,该 Promise
在原始 Promise
解决后会采用相同的值,或在原始 Promise
被拒绝后将拒绝相同的原因。回调函数 onFinally
不接收任何参数,因此它无法知道原始 Promise
的最终状态或值。
使用场景
- 清理工作: 用于执行清理操作,例如关闭文件、释放资源,无论 Promise 是成功还是失败。
const fetchData = () => {
return new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
success ? resolve("数据提取") : reject("数据错误");
}, 1000);
});
};
fetchData()
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("清除操作");
});
- 资源管理: 在打开资源(如数据库连接、文件)时,确保在 Promise 执行结束后正确关闭资源。
const openDatabaseConnection = () => {
return new Promise((resolve, reject) => {
// 打开数据库连接
setTimeout(() => {
const success = Math.random() > 0.5;
success ? resolve("数据库连接打开") : reject("打开出错");
}, 1000);
});
};
openDatabaseConnection()
.then(connection => {
console.log(connection);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("关闭连接");
});
- 代码结构简化:
finally
可以用于在then
或catch
后执行一段代码,而不管 Promise 的状态如何,使代码结构更加简洁。
const fetchData = () => {
return new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
success ? resolve("数据提取成功") : reject("读取失败");
}, 1000);
});
};
fetchData()
.then(data => {
console.log(data);
})
.finally(() => {
console.log("无论如何都执行");
});
Promise.finally()
对于执行清理工作、资源管理以及简化代码结构等场景都非常有用。它使得在 Promise 执行结束时执行一段代码变得更加方便和清晰。
3. Rest/Spread 属性(Object Rest Spread) – 提供给对象的rest参数和扩展运算符。
rest参数与spread扩展运算符在ES6中已经引入,不过只针对数组,在ES9中为对象提供了像数组一样的rest参数和扩展运算符。
Rest/Spread 属性主要用于对象和数组的操作,提供了更便捷的语法来处理集合(数组和对象)的元素。对象的 Rest/Spread 属性允许你更简洁地复制和合并对象。
3.1 Spread 属性 (拓展运算)
在对象字面量或数组字面量中使用三个点(...
)来展开一个对象或数组的元素。
- 在对象中使用 Spread:
const obj1 = {
a: 1, b: 2 };
const obj2 = {
...obj1, c: 3 };
console.log(obj2); // { a: 1, b: 2, c: 3 }
- 在数组中使用 Spread:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // [1, 2, 3, 4, 5]
使用场景
- 对象合并: 用于将多个对象合并为一个新对象。
const person = {
name: "WJB", age: 30 };
const address = {
city: "xian", country: "CN" };
const merged = {
...person, ...address };
console.log(merged);
// { name: "WJB", age: 30, city: "xian", country: "CN" }
- 对象克隆: 用于创建一个对象的浅拷贝。
const original = {
a: 1, b: 2 };
const clone = {
...original };
console.log(clone); // { a: 1, b: 2 }
- 数组合并: 用于将多个数组合并为一个新数组。
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4]
- 数组克隆: 用于创建一个数组的浅拷贝。
const originalArray = [1, 2, 3];
const clonedArray = [...originalArray];
console.log(clonedArray); // [1, 2, 3]
3.2 Rest 属性 (剩余参数)
Rest 属性用于收集对象的剩余属性或数组的剩余元素,将它们放入一个新的对象或数组。
- 在对象中使用 Rest:
const {
a, b, ...rest } = {
a: 1, b: 2, c: 3, d: 4 };
console.log(rest); // { c: 3, d: 4 }
- 在数组中使用 Rest:
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(rest); // [3, 4, 5]
使用场景
- 对象解构中的 Rest: 用于从对象中提取部分属性,剩余的属性放入一个新的对象中。
const person = {
name: "WJB", age: 30, city: "xian" };
const {
name, ...details } = person;
console.log(details); // { age: 30, city: "xian" }
- 数组解构中的 Rest: 用于从数组中提取部分元素,剩余的元素放入一个新的数组中。
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(rest); // [3, 4, 5]
- 函数参数中的 Rest: 用于收集函数的剩余参数,将它们放入一个数组中。
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
Rest/Spread 属性在处理对象和数组时提供了更灵活和简洁的语法,使得操作集合的代码更易读和维护。它们广泛用于处理对象合并、对象克隆、数组合并、数组克隆等场景。
4. 正则表达式改进 – 命名捕获组、后行断言、dotAll、Unicode属性转义
ECMAScript 2018 增加了对正则表达式的几项改进,包括命名捕获组、Lookbehind后行断言、dotAll 模式、Unicode属性转义。
4.1 命名捕获组
在 ECMAScript 2018 中,引入了命名捕获组,允许为捕获组分配一个名称,使得匹配的结果更具有可读性。
语法
const regex = /(?<groupName>pattern)/;
使用场景
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = dateRegex.exec('2024-02-02');
console.log(match.groups.year); // "2024"
console.log(match.groups.month); // "02"
console.log(match.groups.day); // "02"
捕获组的名称分别为 “year”、“month” 和 “day”,使得可以通过 match.groups
对象更方便地访问匹配的结果。
4.2 后行断言
在 ECMAScript 2018 中,引入了后行断言,允许在正则表达式中指定一个匹配要求在匹配的字符串后面出现。
语法
const regex = /(?<=pattern)/;
const behind = /(?<!pattern)/;
使用场景
const A = /(?<=@)\w+/;
const B = /(?<!@)\w+/;
const email = 'user@example.com';
console.log(email.match(A)); // ["example"]
console.log(email.match(B)); // ["user", "example", "com"]
正则表达式 A匹配以 “@” 后面的单词,而 B匹配不在 “@” 后面的单词。这对于一些复杂的匹配需求非常有用。
4.3 Dot-All 模式
在 ECMAScript 2018 中,引入了 s
修饰符,允许点(.
)匹配任何字符,包括换行符 \n
。
语法
const regex = /pattern/s;
使用场景
const dotAllRegex = /pattern/s;
const A = /pattern/;
const text = 'line1\nline2\nline3';
console.log(text.match(dotAllRegex)); // 匹配整个字符串,包括换行符
console.log(text.match(A)); // 只匹配第一行
使用了 s
修饰符的正则表达式 dotAllRegex
匹配整个字符串,而没有使用 s
修饰符的 A只匹配第一行。
4.4 Unicode属性转义
在 ECMAScript 2018 中引入了 Unicode 属性转义,这是一项对正则表达式的增强功能。它允许在正则表达式中使用 Unicode 属性来匹配字符,提供更灵活和强大的模式匹配功能。
语法
Unicode 属性转义使用 \p{Property=Value}
的语法,其中 Property
是 Unicode 属性的名称,Value
是属性值。这可以用于匹配具有特定 Unicode 属性和属性值的字符。
const regex = /\p{Property=Value}/u;
使用场景
1. 匹配具有特定 Unicode 属性的字符
const regex = /\p{Script=Latin}/u;
console.log(regex.test('A')); // true
console.log(regex.test('好')); // false
上述正则表达式匹配具有拉丁脚本属性的字符。它会匹配英文字母 ‘A’,但不会匹配中文字符 ‘好’。
2. 匹配具有 Unicode 数字属性的字符
const regex = /\p{Number}/u;
console.log(regex.test('5')); // true
console.log(regex.test('A')); // false
这个正则表达式匹配具有数字属性的字符。它会匹配数字字符 ‘5’,但不会匹配英文字母 ‘A’。
3. 使用 Unicode 属性进行范围匹配
const regex = /\p{Letter}/u;
console.log(regex.test('A')); // true
console.log(regex.test('好')); // true
console.log(regex.test('5')); // false
这个正则表达式匹配具有字母属性的字符,包括英文字母和中文字符。
Unicode 属性转义提供了一种更精细的匹配方式,允许开发者基于 Unicode 属性进行更具体和细粒度的字符匹配。这在处理多语言文本或需要更复杂字符匹配的情况下非常有用。需要注意的是,使用 Unicode 属性转义时需要使用 u
修饰符启用 Unicode 模式。