ES2020的新特性解读

ES2020的新特性解读

 

ES2020 是 ECMAScript 对应 2020 年的版本。这个版本不像 ES6 (ES2015)那样包含大量新特性。但也添加了许多有趣且有用的特性。本文以简单的代码示例来介绍 ES2020新特性。

 

可选链操作符(Optional Chaining)

可选链 可让我们在查询具有多个层级的对象时,不再需要进行冗余的各种前置校验。

日常开发中,当需要访问嵌套在对象内部好几层的属性时,可能就会得到臭名昭著的错误Uncaught TypeError: Cannot read property...,这种错误,让整段程序运行中止。

 

于是,你就要修改你的代码来处理属性链中每一个可能的undefined对象,比如:

let nestedProp = obj && obj.first && obj.first.second; 

在访问 obj.first.second 之前,要先确认 obj 和 obj.first 的值非 null(且不是 undefined)。

有了可选链式调用 ,可以大量简化类似繁琐的前置校验操作,而且更安全:

let nestedProp = obj?.first?.second; 

如果obj或obj.first是null/undefined,表达式将会短路计算直接返回undefined。

可选链操作符的支持情况:chrome,firefox支持

 

 

空位合并操作符(Nullish coalescing Operator)

当我们查询某个属性时,经常会给没有该属性就设置一个默认的值,比如下面两种方式:

let c = a ? a : b // 方式1 
let c = a || b // 方式2 

这两种方式有个明显的弊端,它都会覆盖所有的假值,如(0, '', false),这些值可能是在某些情况下有效的输入。

let x = { 
    profile: { 
      name: '浪里行舟', 
      age: '' 
    } 
} 
console.log(x.profile.age || 18) //18 

上例中age的属性为空字符串,却被等同为假值,为了解决这个问题,ES2020诞生了个新特性--空位合并操作符,用 ?? 表示。如果表达式在??的左侧运算符求值为 undefined 或 null,就返回其右侧默认值。

let c = a ?? b; 
// 等价于let c = a !== undefined && a !== null ? a : b; 

例如有以下代码:

const x = null; 
const y = x ?? 500; 
console.log(y); // 500 
const n = 0 
const m = n ?? 9000; 
console.log(m) // 0 

空位合并操作符的支持情况:chrome,firefox支持

 

 

Promise.allSettled

我们知道 Promise.all 具有并发执行异步任务的能力。但它的最大问题就是如果参数中的任何一个promise为reject的话,则整个Promise.all 调用会立即终止,并返回一个reject的新的 Promise 对象。

const promises = [ 
Promise.resolve(1), 
Promise.resolve(2), 
Promise.reject('error') 
]; 
​ 
Promise.all(promises) 
.then(responses => console.log(responses)) 
.catch(e => console.log(e)) // "error" 

假如有这样的场景:一个页面有三个区域,分别对应三个独立的接口数据,使用 Promise.all 来并发请求三个接口,如果其中任意一个接口出现异常,状态是reject这会导致页面中该三个区域数据全都无法出来,这个状况我们是无法接受,Promise.allSettled的出现就可以解决这个痛点:

Promise.allSettled([ 
Promise.reject({ code: 500, msg: '服务异常' }), 
Promise.resolve({ code: 200, list: [] }), 
Promise.resolve({ code: 200, list: [] }) 
]).then(res => { 
console.log(res) 
/* 
      0: {status: "rejected", reason: {…}} 
      1: {status: "fulfilled", value: {…}} 
      2: {status: "fulfilled", value: {…}} 
  */ 
// 过滤掉 rejected 状态,尽可能多的保证页面区域数据渲染 
RenderContent( 
  res.filter(el => { 
    return el.status !== 'rejected' 
  }) 
) 
}) 

Promise.allSettled跟Promise.all类似,其参数接受一个Promise的数组,返回一个新的Promise唯一的不同在于,它不会进行短路,也就是说当Promise全部处理完成后,我们可以拿到每个Promise的状态,而不管是否处理成功。

Promise.allSettled的支持情况:chrome,firefox支持

 

 

String.prototype.matchAll

如果一个正则表达式在字符串里面有多个匹配,现在一般使用g修饰符或y修饰符,在循环里面逐一取出。

function collectGroup1 (regExp, str) { 
const matches = [] 
while (true) { 
  const match = regExp.exec(str) 
  if (match === null) break 
  matches.push(match[1]) 
} 
return matches 
} 
console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`)) 
// [ 'foo', 'bar', 'baz' ] 
​

值得注意的是,如果没有修饰符 /g, .exec() 只返回第一个匹配。现在通过String.prototype.matchAll方法,可以一次性取出所有匹配。

function collectGroup1 (regExp, str) { 
let results = [] 
for (const match of str.matchAll(regExp)) { 
  results.push(match[1]) 
} 
return results 
} 
console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`)) 
// ["foo", "bar", "baz"] 
​ 

上面代码中,由于string.matchAll(regex)返回的是遍历器,所以可以用for...of循环取出。

String.prototype.matchAll的支持情况:chrome,firefox支持

 

 

Dynamic import

现在前端打包资源越来越大,前端应用初始化时根本不需要全部加载这些逻辑资源,为了首屏渲染速度更快,很多时候都是动态导入(按需加载)模块,比如懒加载图片等,这样可以帮助您提高应用程序的性能。

其中按需加载这些逻辑资源都一般会在某一个事件回调中去执行:

el.onclick = () => { 
import('/modules/my-module.js') 
  .then(module => { 
    // Do something with the module. 
  }) 
  .catch(err => { 
    // load error; 
  }) 
} 

import()可以用于script脚本中,import(module) 函数可以在任何地方调用。它返回一个解析为模块对象的 promise。

这种使用方式也支持 await 关键字。

let module = await import('/modules/my-module.js'); 

通过动态导入代码,您可以减少应用程序加载所需的时间,并尽可能快地将某些内容返回给用户。

Dynamic import的支持情况:chrome,firefox支持

 

 

BigInt

javascript 在 Math 上一直很糟糕的原因之一是只能安全的表示-(2^53-1)至 2^53-1 范围的值,即Number.MIN_SAFE_INTEGER 至Number.MAX_SAFE_INTEGER,超出这个范围的整数计算或者表示会丢失精度。

var num = Number.MAX_SAFE_INTEGER; // -> 9007199254740991 
​ 
num = num + 1; // -> 9007199254740992 
​ 
// 再次加 +1 后无法正常运算 
num = num + 1; // -> 9007199254740992 
​ 
// 两个不同的值,却返回了true 
9007199254740992 === 9007199254740993 // -> true 

于是 BigInt 应运而生,它是第7个原始类型,可安全地进行大数整型计算。 你可以在BigInt上使用与普通数字相同的运算符,例如 +, -, /, *, %等等。

创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可。例如,123 变为 123n。也可以使用全局方法 BigInt(value) 转化,入参 value 为数字或数字字符串。

const aNumber = 111; 
const aBigInt = BigInt(aNumber); 
aBigInt === 111n // true 
typeof aBigInt === 'bigint' // true 
typeof 111 // "number" 
typeof 111n // "bigint" 

只要在数字末尾加上 n,就可以正确计算大数了:

1234567890123456789n * 123n; 
// -> 151851850485185185047n 

不过有一个问题,在大多数操作中,不能将 BigInt与Number混合使用。比较Number和 BigInt是可以的,但是不能把它们相加。

1n < 2  
// true 
​ 
1n + 2 
// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions 

BigInt的支持情况:chrome,firefox支持

 

 

globalThis

globalThis 是一个全新的标准方法用来获取全局 this 。之前开发者会通过如下的一些方法获取:

  • 全局变量 window:是一个经典的获取全局对象的方法。但是它在 Node.js 和 Web Workers 中并不能使用

  • 全局变量 self:通常只在 Web Workers 和浏览器中生效。但是它不支持 Node.js。一些人会通过判断 self 是否存在识别代码是否运行在 Web Workers 和浏览器中

  • 全局变量 global:只在 Node.js 中生效

过去获取全局对象,可通过一个全局函数:

// ES10之前的解决方案 
const getGlobal = function(){ 
if(typeof self !== 'undefined') return self 
if(typeof window !== 'undefined') return window 
if(typeof global !== 'undefined') return global 
throw new Error('unable to locate global object') 
} 
​ 
// ES10内置 
globalThis.Array(0,1,2) // [0,1,2] 
​ 
// 定义一个全局对象v = { value:true } ,ES10用如下方式定义 
globalThis.v = { value:true } 

而 globalThis 目的就是提供一种标准化方式访问全局对象,有了 globalThis 后,你可以在任意上下文,任意时刻都能获取到全局对象。

如果您在浏览器上,globalThis将为window,如果您在Node上,globalThis则将为global。因此,不再需要考虑不同的环境问题。

// worker.js 
globalThis === self 
// node.js 
globalThis === global 
// browser.js 
globalThis === window 

新提案也规定了,Object.prototype 必须在全局对象的原型链中。下面的代码在最新浏览器中已经会返回 true 了:

Object.prototype.isPrototypeOf(globalThis); // true 

globalThis的支持情况:chrome,firefox支持

 

目前已完成提案

提案完成

最终提案是指已到达第4阶段的提案,并已包含在规范的最新草案中。

提案作者冠军TC39会议记录预计出版年
Array.prototype.includes多米尼克·迪尼科拉多米尼克·德尼科拉
·里克·沃尔德隆
2015年11月2016年
求幂运算符里克·沃尔德隆里克·沃尔德隆2016年一月2016年
Object.values/Object.entries乔丹·哈班德乔丹·哈班德2016年三月2017年
字符串填充乔丹·哈班德乔丹·哈班德
·里克· 沃尔德隆
2016年五月2017年
Object.getOwnPropertyDescriptors乔丹
·哈班德·安德里亚·贾玛基
乔丹
·哈班德·安德里亚·贾玛基
2016年五月2017年
函数参数列表和调用中的尾部逗号杰夫·莫里森杰夫·莫里森2016年七月2017年
异步功能布莱恩·特尔森布莱恩·特尔森2016年七月2017年
共享内存和原子拉斯·汉森拉斯·汉森2017年1月2017年
解除模板字面量限制蒂姆·迪斯尼蒂姆·迪斯尼2017年三月2018年
sdotAll)标志正则表达式马蒂亚斯·拜恩斯布莱恩·特尔森(Brian Terlson)
Mathias Bynens
2017年11月2018年
RegExp命名捕获组Gorkem Yakin
Daniel Ehrenberg
丹尼尔·埃伦伯格(Daniel Ehrenberg)
布莱恩·泰尔森(Brian Terlson)
Mathias Bynens
2017年11月2018年
剩余/价差属性塞巴斯蒂安·马克伯(SebastianMarkbåge)塞巴斯蒂安·马克伯(SebastianMarkbåge)一月20182018年
RegExp后置断言Gorkem亚肯
望加藤
丹尼尔·埃伦伯格
Daniel Ehrenberg
Mathias Bynens
一月20182018年
RegExp Unicode属性转义马蒂亚斯·拜恩斯布莱恩·泰尔森(Brian Terlson)
丹尼尔·埃伦伯格(Daniel Ehrenberg)
Mathias Bynens
一月20182018年
Promise.prototype.finally乔丹·哈班德乔丹·哈班德一月20182018年
异步迭代多米尼克·迪尼科拉多米尼克·迪尼科拉一月20182018年
可选catch绑定迈克尔·费卡拉(Michael Ficarra)迈克尔·费卡拉(Michael Ficarra)2018年五月2019年
JSON超集理查德·吉布森马克·米勒·
马蒂亚斯·拜恩斯
2018年五月2019年
Symbol.prototype.description迈克尔·费卡拉(Michael Ficarra)迈克尔·费卡拉(Michael Ficarra)2018年11月2019年
Function.prototype.toString 修订版迈克尔·费卡拉(Michael Ficarra)迈克尔·费卡拉(Michael Ficarra)2018年11月2019年
Object.fromEntries达里恩·梅勒(Darien Maillet)情人节乔丹·哈班德
·凯文·吉本斯
2019年一月2019年
格式良好的 JSON.stringify理查德·吉布森马蒂亚斯·拜恩斯2019年一月2019年
String.prototype.{trimStart,trimEnd}塞巴斯蒂安·马克伯(SebastianMarkbåge)塞巴斯蒂安·马克伯(SebastianMarkbåge)
Mathias Bynens
2019年一月2019年
Array.prototype.{flat,flatMap}布莱恩·泰尔森(Brian Terlson)
迈克尔·费卡拉(Michael Ficarra)
Mathias Bynens
布莱恩·泰尔森(Brian Terlson)
迈克尔·费卡拉(Michael Ficarra)
2019年一月2019年
String.prototype.matchAll乔丹·哈班德乔丹·哈班德2019年三月2020年
import()多米尼克·迪尼科拉多米尼克·迪尼科拉2019年六月2020年
BigInt丹尼尔·埃伦伯格丹尼尔·埃伦伯格2019年六月2020年
Promise.allSettled杰森·威廉姆斯
·罗伯特·佩米利
Mathias Bynens
马蒂亚斯·拜恩斯2019年七月2020年
globalThis乔丹·哈班德乔丹·哈班德2019年十月2020年
for-in 机械师凯文·吉本斯凯文·吉本斯2019年十二月2020年
可选链接加布里埃尔·伊森伯格
·克劳德·帕奇
·达斯汀·萨维里
加布里埃尔·伊森伯格
·达斯汀
·
萨维里贾斯汀·里奇韦尔丹尼尔·罗森瓦瑟
2019年十二月2020年
空位合并运算符加布里埃尔·伊森伯格加布里埃尔·伊森伯格
贾斯汀
·里奇韦尔丹尼尔·罗森瓦瑟
2019年十二月2020年
import.meta多米尼克·迪尼科拉古斯·卡普兰2020年3月2020年

另请参见有效提案阶段1提案阶段0提案无效提案文档。

 

 

参考文章

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值