我们最喜欢的编程语言JavaScript的第11版规范草案ECMAScript2020中包含了很多新功能。其中一些是小的特性,也有一些特性拥有永久改变我们编写JavaScript方式的潜力。
本文是对这些新功能的简短概述,沏好茶,让我们一起开始吧~
动态引入,import()
ES2015中提到到了static import
语法,即普通的引用语法。让你可以从一个模块导出一个变量,然后,直接在另外一个模块中引用。
下面的代码为静态引用的例子:
// utils.js
export function splitName(name) {
return name.split(" ");
}
// index.js
import { splitName } from "./utils";
console.log(splitName("John Snow"));
这种语法被称为静态引用,是因为你没有办法在运行时通过某种条件来动态的引用某一个模块。但是请注意,这不一定是坏事,静态引用允许你在编译时通过Tree Shaking来优化代码(动态的引入使用到的代码,减少打包的bundle包大小)。
另一方面,如果能够合理的利用动态引用,也可以通过按需加载依赖包的方式,减少bundle包的大小。
新的动态引用的语法看起来像一个函数(但是,他并不是函数),它返回一个promise,这意味着我们可以使用async/await
。来看个例子
// 根据条件动态选择需要加载的模块
const mod = figure.kind === "rectangle" ? "rectangle.js" : "circle.js";
// 等待模块加载完成
const { calcSquare } = await import(mod);
console.log(calcSquare(figure));
空位合并,新运算符 ??
通过||
运算符来设置一个值的默认值有它的缺陷。因为它并不是真正的检查值是否为空
,它仅仅是检查这个值是否为否
。当一个值为false
或者0
时,这个设置默认值的方法就会出问题。
ES2020提供了一种新的运算符??
,它和||
的工作原理很类似,但是,它仅仅在初始值为null
或者undefined
时才会取右侧的值。
这里有一个简单的示例:
const initialVal = 0;
// old way
const myVar = initialVal || 10; // => 10
// new way
const myVar = initialVal ?? 10; // => 0
可选链,Optional chaining
可选链操作符?.
设计的目的是为了让我们在检查多层嵌套的对象的某个属性是否存在时,不需要去写冗长的代码。
const user = { name: "John" };
// 这里会报错 `Uncaught TypeError: Cannot read property 'city' of undefined`
const city = user.address.city;
// 可执行,但是,代码冗长
let city = "Not Set";
if (user.address !== undefined && user.address !== null) {
city = user.address.city;
}
// 可执行并且简洁,但是,需要第三方库的支持
const city = _.get(user, "address.city", "Not Set");
//
const city = user?.address?.city ?? "Not Set";
译者注:这个功能真的不错,再也不需要写一大堆&&
判断啦。。
BigInt
BigInt
是一个新的对象,用来表示大于Number.MAX_SAFE_INTEGER
(2^53-1)的数字。对于普通的应用来说,原来的数字听起来就足够使用了,但是,对于某些数学应用或者机器学习来说,BigInt
就需要派上用场啦。
通过在数字后面增加一个字母n
,即可将一个数字指定为BigInt
。
const x = 9007199254740991n;
// 或者你也可以通过给构造函数传一个字符串来实现
const y = BigInt("9007199254740991234");
BigInt
和普通数字并不能互相转化,因此我们不能混合使用二者。要想使用需要先将2个数强制统一成一个类型。
1 === 1n; // => false
1n + 1; // throws Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
6n << 3; // throws Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
6n << 3n; // 48n
String.matchAll
这里有一个例子,想象一下你有一个很长的字符串,并且你需要提取出来所有以#
开头的词。
const tweet = "#JavaScript is full of #surprises. Both good and bad ones #TIL";
for (h of tweet.matchAll(/(#\w+)/g)) {
console.log(h[0]);
}
// or
const tags = [...tweet.matchAll(/(#\w+)/g)]
译者注:额,string.match也能实现上述功能
matchAll
返回的是一个迭代器,可以通过for..of
来循环它,也可以将它转换成一个数组。
Promise.allSettled
还记得Promise.all
函数么?当所有promise都触发了resolve
状态,它才会进入resolve
状态。但是,当其中一个promise进入了reject
状态,整个Promise.all
就会直接进入reject
状态,无论其他promise是否还处在pending
状态。
Promise.allSettled
的表现和Promise.all
不一样。当所有promise完成工作了,它会进入resolve
状态,无论,这些promise是进入了resolve
或者reject
。它会将所有promise的处理结果都通过resolve函数返回出来。
因此,allSettled
没有reject
状态,他只有pending
和resolve
状态。
下面是一个是现实世界的问题:去除loading框
// const urls = [...]
try {
await Promise.all(urls.map(fetch))
} catch (e) {
// 至少有一个promise进入了reject状态,但是,其他promise可能还处在pending中,这样会导致loading去除的过早
removeLoading()
}
// 使用allSettled
await Promise.allSettled(urls.map(fetch))
removeLoading()
globalThis
在JavaScript中,有一个包含任何东西的巨大上下文。一般来说,在浏览器中它是window
对象。在Node
应用它又变成了global
对象。到了webWorker中,它又变成了self
对象。
新的globalThis
属性消除了环境带来的差异性,你可以使用globalThis
引入而不需要关心你所处的上下文。
如果你认为这个命名有点尴尬,我完全同意您的意见,但是请注意,以self
或global
命名可能会不兼容某些较旧的代码。所以我想我们必须忍受这一点。
关于译者本人
我是一个莫得感情的代码搬运工,现在主要精力会投在更新《Laya2.x游戏引擎入门系列》上,因为实在拖的太久啦,对不住自己年前定的计划。
最近,搞了一个公众号,大家有兴趣的话关注一下,我们一起交流前端知识~
好啦,翻译完毕啦,原文链接在此 What's new in ECMAScript 2020。