赞
踩
前面两篇咱们已经了解了ES6—ES8的常用新特性:
ES新特性系列(一)—— ES的简介与ES6
有兴趣的彦祖们可以跳转了解。本期咱们接着聊ES9、ES10的新特性。
异步迭代的出现主要是为了解决异步数据流的处理问题。
传统Javascript中的for...of...循环只能处理同步的数据结构,我们看下面这个简单的例子:
- const promises = [Promise.resolve('data1'), Promise.resolve('data2'), Promise.resolve('data3')];
-
- for (let promise of promises) {
- console.log(promise);
- }
- // 输出结果
- // Promise {<fulfilled>: 'data1'}
- // Promise {<fulfilled>: 'data2'}
- // Promise {<fulfilled>: 'data3'}
因为上述代码是同步执行,所以输出结果是立即输出,并且输出Promise的结果,而直接就是Promise本身,这显然不是咱们想要的结果。这时候咱们就要看ES9引入的异步迭代: for await...of
循环了。
异步迭代允许我们以同步的编程方式来处理异步的数据。这大大简化了异步编程,并使得代码更易读和理解。同样以上述例子来看看异步迭代处理的结果:
- const promises = [Promise.resolve('data1'), Promise.resolve('data2'), Promise.resolve('data3')];
-
- // 使用 for await...of 循环
- async function processPromises() {
- for await (let result of promises) {
- console.log(result);
- }
- }
-
- processPromises(); // 结果:data1、data2、data3,您可以复制代码在控制台试一下
await关键词允许我们在循环之前等待异步操作得出结果,再进行处理,项目中合理使用异步迭代可以大大提高咱们的代码简洁和可读性。
这个特性大家在项目中应该都经常使用,它的作用相当于给Promise的所有操作做“收尾”的工作,通常用于在 Promise 完成(无论是 fulfilled 还是 rejected)后执行某些操作。
举个简单的例子:如果你给一个提交按钮设置了loading——在提交数据到结果返回之前按钮都是在loading状态,当结果返回时,无论是提交成功还是处理失败,都需要取消按钮的loading状态才行,此时我们就需要用到 .finally() :
- submit(data) { // 确认
- this.loading = true
- method(data).then(res => {
- console.log(res.code)
- }).finally(() => {
- this.loading = false
- })
- }
咱们初学的话可以将它看作是拓展运算符升级版,回忆一下ES6的拓展运算符——它允许在数组中操作,而此时升级后的拓展运算符,它允许我们在对象中对进行操作。
1.Spread 属性:Spread 操作符在对象中的应用,可以用于从一个对象创建一个新对象,同时添加、覆盖或组合属性。例如:
- const obj1 = { a: 1, b: 2, c: 4 };
- const obj2 = { ...obj1, c: 3 };
- const obj3 = { c: 3, ...obj1 };
-
- console.log(obj2) // { a: 1, b: 2, c: 3 }
- console.log(obj3) // { c: 4, a: 1, b: 2 } 你注意到差别了吗?
-
2.Rest 属性:Rest 操作符在对象中的应用,可以用于从一个对象中提取出剩余的属性到一个新的对象中。例如:
- const obj = { a: 1, b: 2, c: 3 };
- const { a, ...rest } = obj; // rest: { b: 2, c: 3 }
这个特性主要是正则表达式上的升级:在这之前,我们只能使用数字索引(如 match[1]
)来访问捕获组。但是,当正则表达式变得复杂,或者你修改了捕获组的顺序,使用数字索引就会变得困难和混乱。例如:
- let re = /(\d{4})-(\d{2})-(\d{2})/;
- let match = re.exec('2022-01-01');
-
- let year = match[1]; // "2022"
- let month = match[2]; // "01"
- let day = match[3]; // "01"
然后我们可能有项目需要要调整时间格式:
- let re = /(\d{2})-(\d{2})-(\d{4})/;
- let match = re.exec('01-01-2022');
-
- let month = match[1]; // "01"
- let day = match[2]; // "01"
- let year = match[3]; // "2022"
这时我们发现输出的month、year、day顺序完全不对了。有的彦祖说我一个一个手动再把索引改了不就得了,但是如果是100个数据呢?如果是项目全局需要改呢?这样傻傻改索引很麻烦且容易出错。接下来咱们用命名捕获组来解决该问题:
- let re = /(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})/;
- let match = re.exec('01-01-2022');
-
- let year = match.groups.year; // "2022"
- let month = match.groups.month; // "01"
- let day = match.groups.day; // "01"
在这个例子中,(?<year>\d{4})、(?<month>\d{2}) 和 (?<day>\d{2}) 是命名捕获组。?<year>、?<month> 和 ?<day> 是捕获组的名称,而 \d{4}、\d{2} 和 \d{2} 是捕获组的模式。在执行匹配后,你可以通过 match.groups.year、match.groups.month 和 match.groups.day 来访问捕获组的值。
由此,无论上面的正则中的顺序如何修改,我们下面的year、month、day都可以准确的获取到对应的值,大大降低了我们对代码的维护成本,并且可读性强。
还记得咱们再上一篇ES8的特性中讲到过Object.entries()的方法吗?它可以返回对象键值对的二维数组:
- const obj = {a: 1, b: 2, c: 3}
- console.log(Object.entries(obj)); // 输出:[['a', 1], ['b', 2], ['c', 3]]
Object.fromEntries()就是Object.entries()的反向操作:它可以将一个二维的键值对数组转化为对象:
- const arr = [['a', 1], ['b', 2], ['c', 3]]
- console.log(Object.fromEntries(arr)) // {a: 1, b: 2, c: 3}
两个都是数组扁平化的方法,flat() 方法用于将嵌套的数组“拉平”。
- const arr = [1, [2, [3, [4]], 5]];
-
- console.log(arr.flat()); // [1, 2,[3,[4]], 5]
- console.log(arr.flat(2)); // [1, 2,3,[4], 5]
- console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5]
Array.prototype.flat() 可以输入参数,默认为1。(其中的Infinity相当于一个全局的无穷数)。
flatMap() 是flat()和map()合起来的方法:先执行map操作,然后再对结果进行flat()操作:
- const arr = [1, 2, 3, 4];
- const result = arr.flatMap(x => [x, x * 2]);
-
- console.log(result); // 输出: [1, 2, 2, 4, 3, 6, 4, 8]
这两个方法用于去除字符串开始和结束处的空白字符。
- const str = ' foo ';
- console.log(str.trimStart()); // "foo "
- console.log(str.trimEnd()); // " foo"
之前,JavaScript 中的所有数字都是 Number 类型,这种类型只能安全地表示 -(2^53 - 1) 到 2^53 - 1 范围内的整数。超出这个范围,Number 类型将无法精确表示整数。但是BigInt可以解决,不过项目中应该也不会太常用,了解即可:
- // 使用 BigInt 字面量创建 BigInt
- const a = 1234567890123456789012345678901234567890n;
-
- // 使用 BigInt 函数创建 BigInt
- const b = BigInt("1234567890123456789012345678901234567890");
-
- console.log(a === b); // 输出:true
-
- // BigInt 还支持常见的算数运算
- const c = a + b;
- console.log(c); // 输出:2469135780246913578024691357802469135780n
在之前的 ECMAScript 版本中,catch
子句必须包含一个异常变量。而在 ES10 中,如果你不需要访问这个异常变量,你可以省略它。
- try {
- throw 'error';
- } catch {
- console.log('An error occurred');
- }
在 ES10 中,Function.prototype.toString() 方法返回的结果现在包含函数的注释。
- function exampleFunction() {
- // 这是一个示例函数
- console.log('Hello, world!');
- }
-
- console.log(exampleFunction.toString());
-
- // 输出为
- "function exampleFunction() {
- // 这是一个示例函数
- console.log('Hello, world!');
- }"
-
一个新的全局对象,不论当前代码在何处运行,都可以用它来访问全局对象。
- globalThis.myGlobalVariable = 'Hello, world!';
-
- console.log(myGlobalVariable); // 输出:'Hello, world!'
在这个例子中,myGlobalVariable
是一个全局变量,可以在任何地方访问,无论是在函数内部,还是在模块中。
它提供了一种获取 Symbol 描述的方法。
- let mySymbol = Symbol('my description');
- console.log(mySymbol.description); // 输出 "my description"
本期ES9、ES10的常用新特性就给大家介绍到这里了,如果大家觉得不错,就请点赞关注一下吧。后续将继续为大家带来前端相关的内容,如有疑问,可以评论回复。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。