ECMAScript 2020新增特性

前言

TC39 委员会于近期批准了 ECMAScript 2020(即 ES2020)候选提案,即经审定最终添加到 JavaScript 语言中的特性集。ES2020 候选提案是今年六月提交 ECMA 大会(General Assembly)的审批汇总。


1. Promise.allSettled

Promise.all 缺陷

都知道 Promise.all 具有并发执行异步任务的能力。

先复习一下Promise.all 的用法

let p1 = Promise.resolve({
    code: 200,
    list: ["数据1"]
})
let p2 = Promise.resolve({
    code: 200,
    list: ["数据2"]
})
let p3 = Promise.resolve({
    code: 200,
    list: ["数据3"]
})
​
Promise.all([p1,p2,p3]).then((result) => {
    // all方法并发的三个promise都是成功状态会执行
    console.log(result);
    /*
        返回的结果: [{…}, {…}, {…}]
    */
}).catch((error) => {
    console.log(error);
})
​

但时Promise.all 方法最大问题就是如果其中某个任务出现异常(reject),所有任务都会挂掉,Promise直接进入 reject 状态。

let p1 = Promise.resolve({
      code: 200,
      list: ["数据2"]
  })
  let p2 = Promise.reject({
      code: 500,
      errorMsg:"服务器错误"
  })
​
  let p3 = Promise.resolve({
      code: 200,
      list: ["数据3"]
  })
​
  Promise.all([p1,p2,p3]).then((result) => {
     // 如果有一个promise 是reject,则不会执行这个回调函数
      console.log(result);   
  }).catch((error) => {
      console.log(error);
      // 因为p2 是reject 所以会执行这个回到函数
      // {code: 500,errorMsg:"服务器错误"}
  })

如果小伙伴们在测试代码的时候,记得重新打开页面,不要刷新.

想象这个场景:在你的项目中,使用 Promise.all 来并发三个接口,每个接口都单独对应一个接口,请求数据, 如果其中任意一个接口服务异常,状态是reject,这会导致三个区域数据全都无法渲染出来,因为任何 reject 都会进入catch回调, 很明显,这是无法接受的.

Promise 在 ES2020 中新增了 Promise.allSettled() ,就能好的 帮我们解决这个问题,Promise.allSetted 方法,无论一个任务是正常或是异常,都会返回对应的状态(fulfilled或者rejected)与结果(value),

let p1 = Promise.resolve({
    code: 200,
    list: ["数据2"]
})
let p2 = Promise.reject({
    code: 500,
    errorMsg:"服务器错误"
})
​
let p3 = Promise.resolve({
    code: 200,
    list: ["数据3"]
})
​
Promise.allSettled([p1,p2,p3]).then((result) => {
            // Promise.allSettled会在这里中处理并发promise状态和结果
    console.log(result);
    /**
                返回的结果:
               [
                 {status: "fulfilled", value: {…}}
                 {status: "rejected", reason: {…}}
                 {status: "fulfilled", value: {…}}
               ]
            */
​
})

这样我们在then方法中得到所有结果以后,我们就可以使用fill来过滤状态为rejected掉的数据

2. String.prototype.matchAll


String.prototype上的match()方法仅返回完全匹配,但是没有返回关于特定正则组的任意信息。String.prototype.matchAll可以返回比match()多很多的信息。返回的迭代器除了精确匹配外还给了我们访问所有的正则匹配捕获组。

还节的match方法吗?看下面一段代码

let str = "<div>this is div</div><p>this is JS</p>";
let reg = /<\w+>(.*?)<\/\w+>/g;
console.log(str.match(reg));
/**
  返回结果:
     ["<div>this is div</div>", "<p>this is JS</p>"]
*/

我们都知道,match是可以正常匹配到所有匹配项,但却没办法匹配到子项(group)。如果想要匹配子项,那么需要把全局匹配 /g 标识去掉。

let str = "<div>this is div</div><p>this is JS</p>";
let reg = /<\w+>(.*?)<\/\w+>/;
console.log(str.match(reg));
/**
 [
    0: "<div>this is div</div>"
    1: "this is div"
    groups: undefined
    index: 0
    input: "<div>this is div</div><p>this is JS</p>"
    length: 2
  ]
**/  

这样可以获取到匹配的父项,包括子项(group),但只能获取到第一个满足的匹配字符。

如果既想要匹配所有匹配项,又想要匹配子项,那么 match() 是无法满足的。ES2020 提供了 matchAll() 方法.

注意matchAll方法返回的是迭代器,因此我们需要遍历获取结果

let str = '<div>this is div</div><p>this is JS</p>';
let reg = /<\w+>(.*?)<\/\w+>/g;
let allMatchs = str.matchAll(reg);
​
for(let match of allMatchs){
    console.log(match)
}
/**
  第一次遍历的结果:
    [
      0: "<div>this is div</div>"
      1: "this is div"
      groups: undefined
      index: 0
      input: "<div>this is div</div><p>this is JS</p>"
      length: 2
    ]
  第二次遍历的结果:
    [
      0: "<p>this is JS</p>"
      1: "this is JS"
      groups: undefined
      index: 22
      input: "<div>this is div</div><p>this is JS</p>"
      length: 2
    ]
**/


3. import()


目前前端项目打包的资源越来越大,但应用初始化时资源并不需要全量加载,为了提高页面性能,往往需要按需加载资源。Domenic Denicola提案的动态导入可以实现按需加载。这个类似函数的格式(不是继承自Function .prototype)返回一个很强大的promise。使用场景比如: 按需导入,在一个脚本中计算模块名并加载执行变得可能。

element.onclick = () =>{
   import("/js/helpers.js")
    .then((module) =>{
        console.log(module)
    })
    .chatch((err) => {
        // load err
        console.log(err)
    })
}

因此我们也可以使用async异步函数来配置处理

element.onclick = async () =>{
   let module = await import("/js/helpers.js")
   // 处理 导入module模块
}


4. BigInt


JavaScript 中 Number 类型都保存为 64 位浮点数,精确度只能到 53 位,也就是说Js 中 Number类型只能安全的表示-(2^53-1)至 2^53-1 范的值,超出这个范围的整数计算或者表示会丢失精度。

console.log(Math.pow(2, 53));   // 9007199254740992
console.log(Math.pow(2, 53) + 1);  // 9007199254740992
console.log(Math.pow(2, 53) === Math.pow(2, 53) + 1); // true

且无法正确表示大于或等于 2^1024 的数值。

console.log(Math.pow(2, 1023));  // 8.98846567431158e+307
console.log(Math.pow(2, 1024));  // Infinity

ES2020 中引入了新的数据类型 BigInt, 让Number.MAXSAFEINTEGER不再是JavaScript中的一个限制。BigInt是一个能表示任意精度整数的新基础类型。你可以通过使用BigInt方法或者在一个数字后添加n后缀来把一个数字转换为一个新的bigint类型。

// 使用BigInt
// 1. 字面量方式在数字字面量后面加n
let num = 123n;
console.log(num);   // 123n
console.log(typeof num); // bigint
​
// 2. 函数执行的方式
let number = BigInt(Math.pow(2, 53) + 1);
console.log(number);  // 9007199254740992n
console.log(typeof number); // bigint

那么我们看看BigInt处理大型数字相加处理

let number = BigInt(Math.pow(2, 53));
letnumber2 = BigInt(Math.pow(2, 53));
console.log(number);  // 9007199254740992n
console.log(number2); // 9007199254740992n
​
letnum = number + number2;
console.log(num);      // 18014398509481984n
console.log(num.toString());  // 18014398509481984

注意:

BigInt 是一种新的数据原始(primitive)类型。

5. for-in机制


ECMAScript遗留了一个关于for-in循环顺序的详细描述。在 ECMA-262 5rd Edition 中对遍历机制又进行了调整,并未并且规定具体的规则,不同浏览器有不同的实现,这导致对属性的遍历顺序存在不一致的问题。ES2020 中要求对象的遍历实现上,各浏览器要保持一致。


6. 可选链(Optional chaining)


以前在处理多层对象属性值获取的时候,通常需要对各层级的属性进行校验.

例如

let name = user && user.info && user.info.name

这是一种丑陋但又不得不做的前置校验,否则很容易命中 Uncaught TypeError: Cannot read property… 这种错误,这极有可能让你整个应用挂掉。

对此,ES2020 进行了优化,可以通过 ?. 来简化校验。

?. 操作符与 . 类似,两者的区别在于,?. 在获取对象属性时,如果其引用对象为 null 或 undefined,则表达式会发生短路,直接返回 undefined。

示例代码如下:

// 1. 能正确找到属性值
let user = {
    info:{
        name:"小明"
    }
}
var name = user?.info?.name;
console.log(name);   // 小明
​
// 2. 属性值不存在
let user2 = {
    info:{
        city:"上海"
    }
}
var name2 = user2?.info?.name;
console.log(name2);   // undefined


7. 空值合并运算符(Nullish coalescing Operator)


在JavaScript中我们经常会遇到给某个变量或者对象的属性添加默认值

示例:

// 三目运算符处理默认值
const name = user.name ? user.name : "默认名称";
​
// 逻辑运算符中的短路算法处理默认值
const name = user.name || "默认名称" 

ES2020 新增了更简洁的空值合并操作符(??),左侧值为 null 或 undefined 时返回右侧默认值

const name = user.name ?? "默认名称" 

但对于逻辑或操作符来说,''、0 都会转化为 false,容易产生逻辑错误。在业务上,大多是是想判断变量是否为 undefined 或者 null。

因此要注意,

// user.name的值会有可能会进行隐式类型转行,为0的时候,也会只用默认值
const name = user.name || "默认名称" ;
​
// ES2020新增的?? 运算符 有且仅当user.name的为null或者undefined时
// 才会启用默认值
const name = user.name ?? "默认名称" 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352