前言
ES6(ES2015)大家已经用了很久,ES2020 是 ECMAScript 对应 2020 年的版本。
这个版本不像 ES6 那样包含了大批量的新特性。但也添加了很多实用方便的特性。
接下来一起看下这些简单有用的新特性吧~
1. 可选链操作符(Optional Chaining)
可选链操作符 可以让我们在查询多个层级的对象时,不需要再进行各种冗余的前置校验
日常开发中,我们经常遇到需要访问在对象内部嵌套了好几层的属性,一旦有某个层级的对象找不到的时候,就会遇到下图这样的错误:
let num = obj && obj.first && obj.first.second;
在访问 obj.first.second 之前要确认 obj 和 obj.first 的值非 null 且不是 undefined。 有了可选链式调用,可以大量简化类似繁琐的前置校验操作,可以改成如下方式:
let num = obj?.first?.second;
如果obj或obj.first是null/undefined,表达式将会短路计算直接返回undefined
浏览器的支持情况
2. 空位合并操作符(Nullish coalescing Operator)
我们在工作中经常会遇到一种情况,当某个值不存在的时候会给一个默认的值,我们经常会采用下面的方式:
let c = a || b
这种方式有个明显的弊端,他会覆盖所有的假值,比如 0、''、false、null、undefined 等,这些值在某些情况下可能是有效的数据。比如下面的情况:
let person = {
info: {
name: '小乔',
age: '18',
num: 0
}
}
console.log(person.info.num || 1) //1
上面的示例中,num 属性的值为 0,却被等同于假值。 为了解决这个问题,ES2020 诞生了一个新特性 —— 空位合并操作符,用 ?? 表示,如果表达式在 ?? 左侧的运算符求值为 undefined 或者 null,就返回其右侧的默认值。可以看下以下的案例:
const x = null;
const y = x ?? 500;
console.log(y); // 500
const n = 0
const m = n ?? 9000;
console.log(m) // 0
浏览器的支持情况
3. Promise.allSettled
我们知道 Promise.all 具有并发执行异步任务的能力。但它最大的问题就是 如果参数中的任何一个 promise 为reject 的话,整个 Promise.all 调用会立即停止,并返回一个 reject 的新的 Promise 对象。看下面的例子:
const promiseList = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject('error')
];
Promise.all(promiseList)
.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)
})
看一下它的返回结果:
浏览器的支持情况
4. String.prototype.matchAll
使用方式:给定一个字符串和一个正则表达式,.matchAll()为所有匹配的匹配对象返回一个迭代器。
const matchIterator = str.matchAll(regExp);
如果一个正则表达式在字符串里面有多个匹配,现在一般使用 /g 修饰符,在循环中逐一取出:
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"]
浏览器的支持情况:
5. 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');
浏览器的支持情况:
6. globalThis
globalThis 是一个全新的标准方法,用来获取全局的 对象。之前我们一般会通过如下的一些方法获取:
- 全局变量 window
是一个经典的获取全局对象的方法。但是它在 Node.js 和 Web Workers 中并不能使用 - 全局变量 self
通常只在 Web Workers 和浏览器中生效。但是它不支持 Node.js。一些人会通过判断 self 是否存在识别代码是否运行在 Web Workers 和浏览器中 - 全局变量 global
只在 Node.js 中生效
如果我们想写一套通用的代码来获取全局对象,一般会封装一个如下的函数:
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');
}
而 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
浏览器的支持情况
参考文章
- TC39 Proposals
- MDN文档
- 5 ECMAScript Proposals To Look Out For In ES2020
- ES2020 Features in simple examples