原文地址:https://alligator.io/js/es2020/?luicode=10000011&lfid=1076031400854834&u=https%3A%2F%2Fallitator.io%2Fjs%2Fes2020%2F 作者:Joshua Hall
JavaScript经过近几年的快速发展和更新,现在是时候来梳理一下最新的ES2020的重大更新了。在本文中,我们将讨论一些伴随ES2020到来的最新的最重大的JS新特性。
初始化
由于用户都比较懒,他们甚至都不愿意升级一下自己的浏览器来使程序员的开发工作更简单一些,因此程序员不得不使用babel工具来将一些新的JS特性兼容到较低版本的不支持新特性的用户浏览器中。出于最简单的目的,我在此使用Parcel bundle工具包来使程序尽可能快的运行。在命令行界面输入如下命令:
$yarn add parcel-bundle
在package.json文件中增加如下属性:
"scripts": {
"start" : "parcel index.html"
},
很不幸,由于目前我们的开发环境中还没有关于ES2020的相关预设,因此这样的写法大大的超出了当前的版本,还不能直接这样使用。我们需要将一些预设添加到.babelrc文件中保存,之后Parcel就会将我们所有想要的都初始化。.babelrc中添加的设置如下:
{
"plugins" : [
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-syntax-bigint"
]
}
准备工作就绪,那么下面就让我们看看ES2020为我们准备啥新奇有趣的新特性吧!
类的私有变量
类的一个主要作用就是将我们的代码进行封装以便在不同的模块中进行调用。当需要在不同的地方调用相同的功能时,我们就需要考虑将该功能封装成一个类,而不是将所有的东西都做成全局的以方便调用,这样容易引起代码混乱。现在,通过在变量或者方法前面增加一个简单的哈希符号我们就能限制类的变量或者方法只能在类的内部进行调用。代码如下:
class Message{
#message = "Howdy"
greet() { console.log(this.#message) }
}
const greeting = new Message()
greeting.greet() //Howdy
console.log(greeting.#message) //Private name #message is not defined
Promise.allSettled
在实际开发中,当我们使用多个promise时,尤其是当多个promise之间还相互依赖时,我们就需要记录它们之间究竟发生了什么以便更好地进行代码调试和纠错。通过使用Promise.allSettled,我们会创建一个新的promise,这个promise只有在所有的和它相关promise都完成的时候才会返回。它允许我们访问一个数组以提供所有已经完成promise的相关数据。代码如下:
const p1 = new Promise((res, rej) => setTimeout(res, 1000));
const p2 = new Promise((res, rej) => setTimeout(rej, 1000));
Promise.allSettled([p1, p2]).then(data => console.log(data));
/*打印
[
Object { status: "fulfilled", value: undefined},
Object { status: "rejected", value: undefined}
]
*/
空值合并符
由于JavaScript是动态类型,因此当我们定义变量的时候时刻都要在心里记住该值是真值还是假值。很多时候,我们会定义一个拥有多个值的对象,有时候我们需要允许一些技术层面的假值存在,比如空字符串或者数字0。设置默认值有一个不好的地方,就是它很容易受到干扰,被非法数值重写掉。请看下面代码:
let person = {
profile: {
name: "",
age: 0
}
};
console.log(person.profile.name || "Anonymous");
console.log(person.profile.age || 18);
相对于双竖杠符号来说,双问号操作符具有更严格的类型检查,它只允许值为null或者undefined的时候才会使用默认值。0或者空字符串都不会使用默认值。如下面代码所示:
console.log(person.profile.name ?? "Anonymous"); //""
console.log(person.profile.age ?? 18) //0
可选链式操作符
和空值合并符的情况一样,当JavaScript遇到假值的情况时不会不会表现得向我们预想得那样。当遇到一个undefined的变量时,我们可以返回一个默认值,但是如果如果我们的访问路径本身就一个undefined时,我们该怎么办呢?
通过在点号前面添加一个问号标记,我们就能确保不管路径中任何部分是undefined,我们都能正确优雅地处理它。代码如下:
let person = {};
console.log(person.profile.name ?? "Anonymous"); //person.profile is undefined
console.log(person?.profile?.name ?? "Anonymous"); //Anonymous
console.log(person?.profile?.age ?? 18); //18
BigInt
我们在这里并不讨论具体的技术细节。但是考虑到JavaScript处理数字的方式,当出现一些不确定的因素时,就会涉及到一些更高级的技术细节。JavaScript所能处理的最大数字时2^53,我们可以通过MAX_SOFE_INTEGER进行查看。代码如下:
const max= Number.MAX_SAFE_INTEGER;
console.log(max); //9007199254740991
上述代码没有什么问题,但是接下来,事情就变得有点奇怪了。请看下面的代码:
console.log(max + 1); //9007199254740992
console.log(max + 2); //9007199254740992
console.log(max + 3); //9007199254740994
console.log(Math.pow(2, 53) == Math(2, 53) + 1); //true
我们可以将上述代码用BitInt进行实现。通过将字符n置于算术表达式后面,我们就能使用和计算真正变态级大数值。由于我们不能混淆普通的数值和BitInt数值,因此所有关于数值计算的的函数都需要以BigInt的类型再实现一遍。参考如下代码:
const bigNum = 1000000000000000000000000000000n;
console.log(bigNum * 2n); //2000000000000000000000000000000n
动态Import
如果一个件中有大量的的公共函数,实际上它们中的一部分又很少用到,那么所有对于该文件的引用就会造成大量的资源浪费。现在,我们通过使用async/await来动态引入那些我们需要调用的资源,以减少资源引入的浪费。
提示:目前我们的Parcel设置还不支持该功能,我们需要切换到Node.js环境中。
请看下面代码:
math.js
const add= (num1, num2) => num1 + num2;
export { add };
index.js
const doMath = async (num1, num2) +> {
if (num1 && num2){
const math = await import('./math.js');
console.log(math.add(5, 10));
};
};
doMath(4, 2);
总结
现在,你大概已经为JavaScript的这些新特性感到不可思议了,与此同时你的同事可能还因为不够了解而对这些特性感到常困惑。(当然,除非他们已经看过了本人写的这篇文章,嘿嘿。。)
网友评论