种草 ES2020 新特性,真的学不动了

做者:李大雷
https://juejin.im/post/5e09ca40518825499a5abff7


这几年,Ecma TC39 一年一次更新 ECMAScript 规范标准,截止目前,如下特性已进入 finished 状态。如今带你们体验种草 ES2020 新特性。
前端

一:Promise.allSettled

Promise.all 缺陷

都知道 Promise.all 具备并发执行异步任务的能力。但它的最大问题就是若是其中某个任务出现异常(reject),全部任务都会挂掉,Promise 直接进入 reject 状态。webpack

想象这个场景:你的页面有三个区域,分别对应三个独立的接口数据,使用 Promise.all 来并发三个接口,若是其中任意一个接口服务异常,状态是 reject,这会致使页面中该三个区域数据全都没法渲染出来,由于任何 reject 都会进入 catch 回调, 很明显,这是没法接受的,以下:git

Promise.all([
Promise.reject({code: 500, msg: '服务异常'}),
Promise.resolve({ code: 200, list: []}),
Promise.resolve({code: 200, list: []})
])
.then((ret) => {
// 若是其中一个任务是 reject,则不会执行到这个回调。
RenderContent(ret);
})
.catch((error) => {
// 本例中会执行到这个回调
// error: {code: 500, msg: "服务异常"}
})

Promise.allSettled 的优点

咱们须要一种机制,若是并发任务中,不管一个任务正常或者异常,都会返回对应的的状态(fulfilled 或者 rejected)与结果(业务 value 或者 拒因 reason),在 then 里面经过 filter 来过滤出想要的业务逻辑结果,这就能最大限度的保障业务当前状态的可访问性,而 Promise.allSettled 就是解决这问题的。github

Promise.allSettled([
Promise.reject({code: 500, msg: '服务异常'}),
Promise.resolve({ code: 200, list: []}),
Promise.resolve({code: 200, list: []})
])
.then((ret) => {
/*
0: {status: "rejected", reason: {...}}
1: {status: "fulfilled", value: {...}}
2: {status: "fulfilled", value: {...}}
*/

// 过滤掉 rejected 状态,尽量多的保证页面区域数据渲染
RenderContent(ret.filter((el) => {
return el.status !== 'rejected';
}));
});

二:可选链

可选链 可以让咱们在查询具备多层级的对象时,再也不须要进行冗余的各类前置校验。web

平常开发中,咱们常常会遇到这种查询express

var name = user && user.info && user.info.name;

又或是这种数组

var age = user && user.info && user.info.getAge && user.info.getAge();

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

用了 Optional Chaining ,上面代码会变成微信

var name = user?.info?.name;
var age = user?.info?.getAge?.();

可选链中的 ? 表示若是问号左边表达式有值, 就会继续查询问号后面的字段。根据上面能够看出,用可选链能够大量简化相似繁琐的前置校验操做,并且更安全。并发

三:空值合并运算符

当咱们查询某个属性时,常常会遇到,若是没有该属性就会设置一个默认的值。好比下面代码中查询玩家等级。

var level = (user.data && user.data.level) || '暂无等级';

在 JS 中,空字符串、0 等,当进行逻辑操做符判断时,会自动转化为 false。在上面的代码里,若是玩家等级自己就是 0 级, 变量 level 就会被赋值 暂无等级 字符串,这是逻辑错误。

var level;
if (typeof user.level === 'number') {
level = user.level;
} else if (!user.level) {
level = '暂无等级';
} else {
level = user.level;
}

来看看用空值合并运算符如何处理

// {
// "level": 0
// }
var level = `${user.level}级` ?? '暂无等级';
// level -> '0级'

用空值合并运算在逻辑正确的前提下,代码更加简洁。

空值合并运算符 与 可选链 相结合,能够很轻松处理多级查询并赋予默认值问题。

var level = user.data?.level ?? '暂无等级';

四:dynamic-import

按需 import 提案几年前就已提出,现在终于能进入 ES 正式规范。这里我的理解成 "按需" 更为贴切。现代前端打包资源愈来愈大,打包成几 M 的 JS 资源已成常态,而每每前端应用初始化时根本不须要全量加载逻辑资源,为了首屏渲染速度更快,不少时候都是按需加载,好比懒加载图片等。而这些按需执行逻辑资源都体如今某一个事件回调中去加载。

el.onclick = () => {
import(`/path/current-logic.js`)
.then((module) => {
module.doSomthing();
})
.catch((err) => {
// load error;
})
}

固然,webpack 目前已很好的支持了该特性。

五:globalThis

JavaScript 在不一样的环境获取全局对象有不一样的方式,NodeJS 中经过 global, Web 中经过 windowself 等,有些甚至经过 this 获取,但经过 this 是及其危险的,this 在 JavaScript 中异常复杂,它严重依赖当前的执行上下文,这些无疑增长了获取全局对象的复杂性。

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

var 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');
};

var globals = getGlobal();

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis

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

六:BigInt

JavaScript 中 Number 类型只能安全的表示-(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

为解决此问题,ES2020 提供一种新的数据类型:BigInt。使用 BigInt 有两种方式:

  1. 在整数字面量后面加 n
var bigIntNum = 9007199254740993n;
  1. 使用  BigInt 函数。
var bigIntNum = BigInt(9007199254740);
var anOtherBigIntNum = BigInt('9007199254740993');

经过 BigInt, 咱们能够安全的进行大数整型计算。

var bigNumRet = 9007199254740993n + 9007199254740993n; // -> -> 18014398509481986n

bigNumRet.toString(); // -> '18014398509481986'

注意:

  1. BigInt 是一种新的数据原始(primitive)类型。
typeof 9007199254740993n; // -> 'bigint'
  1. 尽量避免经过调用函数  BigInt 方式来实例化超大整型。由于参数的字面量实际也是  Number 类型的一次实例化,超出安全范围的数字,可能会引发精度丢失。

七:String.prototype.matchAll

The matchAll() method returns an iterator of all results matching a string against a regular expression, including capturing groups. ——MDN

思考下面代码:

var str = '<text>JS</text><text>正则</text>';
var reg = /<\w+>(.*?)<\/\w+>/g;

console.log(str.match(reg));
// -> ["<text>JS</text>", "<text>正则</text>"]

能够看出返回的数组里包含了父匹配项,但未匹配到子项(group)。移除全局搜索符"g"试试。

var str = '<text>JS</text><text>正则</text>';
// 注意这里没有全局搜素标示符"g"
var reg = /<\w+>(.*?)<\/\w+>/;
console.log(str.match(reg));

// 上面会打印出
/*
[
"<text>JS</text>",
"JS",
index: 0,
input:
"<text>JS</text><text>正则</text>",
groups: undefined
]
*/

这样能够获取到匹配的父项,包括子项(group),但只能获取到第一个知足的匹配字符。能看出上面没法匹配到<text>正则</text>

若是获取到全局全部匹配项,包括子项呢?

ES2020 提供了一种简易的方式:String.prototype.matchAll, 该方法会返回一个迭代器。

var str = '<text>JS</text><text>正则</text>';
var allMatchs = str.matchAll(/<\w+>(.*?)<\/\w+>/g);

for (const match of allMatchs) {
console.log(match);
}

/*
第一次迭代返回:
[
"<text>JS</text>",
"JS",
index: 0,
input: "<text>JS</text><text>正则</text>",
groups: undefined
]

第二次迭代返回:
[
"<text>正则</text>",
"正则",
index: 15,
input: "<text>JS</text><text>正则</text>",
groups: undefined
]
*/

能看出每次迭代中可获取全部的匹配,以及本次匹配的成功的一些其余元信息。

参考资料

  1. finished-proposals [1]
  2. TC39 Proposals [2]


❤️ 看完三件事

若是你以为这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 关注个人博客 https://github.com/SHERlocked93/blog,让咱们成为长期关系

  3. 关注公众号「前端下午茶」,持续为你推送精选好文,也能够加我为好友,随时聊骚。

点这,与你们一块儿分享本文吧~


本文分享自微信公众号 - 前端下午茶(qianduanxiawucha)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索