• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

ES2020,你需要知道的一切内容!

开发技术 开发技术 5个月前 (05-12) 104次浏览

并非是Google,或者是其他权力巅峰的人。JavaScript规范由称为TC39的委员会进行管理和迭代。TC39由各种开发人员,学术界人士和平台爱好者组成。

TC39每年约召开6次会议,大部分在美国,但在欧洲也举行。他们与社区合作,接受有关JavaScript新功能的建议,并逐步处理JavaScript语言建议的四个“阶段”。这 四个阶段 如下:

Stage 0: strawman一种推进ECMAScript发展的自由形式,任何TC39成员,或者注册为TC39贡献者的会员,都可以提交。通常通过针对 TC39 ECMAScript GitHub存储库 提高PR来完成此操作

Stage 1: proposal该阶段产生一个正式的提案。

  • 确定一个 带头人 来负责该提案,带头人或者联合带头人必须是TC39的成员。
  • 描述清楚要解决的问题,解决方案中必须包含例子,API以及关于相关的语义和算法。
  • 潜在问题也应该指出来,例如与其他特性的关系,实现它所面临的挑战。
  • polyfill和demo也是必要的。

围绕提案创建了一个公共GitHub存储库,其中包含示例,高级API,基本原理和潜在问题。

Stage 2: draft草案是规范的第一个版本,与最终标准中包含的特性不会有太大差别。 草案之后,原则上只接受增量修改。

  • 草案中包含新增特性语法和语义的,尽可能的完善的形式说明,允许包含一些待办事项或者占位符。
  • 必须包含 2个实验性的具体实现 ,其中一个可以是用转译器实现的,例如Babel。

“草案”阶段意味着需要确定提案的所有语法和语义。这涉及使用您将在JavaScript规范本身中看到的正式规范语言描述提案功能。

Stage 3: candidate候选阶段,获得具体实现和用户的反馈。 此后,只有在实现和使用过程中出现了重大问题才会修改。

  • 规范文档必须是完整的,评审人和ECMAScript的编辑要在规范上签字。
  • 至少要有 两个符合规范的具体实现

在此阶段,责任在于社区。开发人员应使用该功能并提供反馈,这只有通过在实际开发中使用它才能实现。

Stage 4: finished已经准备就绪,该特性会出现在年度发布的规范之中。

  • 通过 Test 262 的 验收测试
  • 有2个通过测试的实现,以获取使用过程中的重要实践经验。
  • ECMAScript的编辑必须规范上的签字。

尘埃落定。该建议已在社区中的实际实施中得到了很好的测试。本提案将包含在下一版ECMAScript标准中,并将被数以百万计的人使用。

下面就是es2020新特性的介绍!

String.prototype.matchAll

String.prototype.matchAll 是一个实用函数,用于获取特定正则表达式的 所有 匹配项(包括捕获组,这将在后面说明)。在ES2020之前如何解决此问题?让我们来看一个简单的示例并进行迭代:

const test = "climbing, oranges, jumping, flying, carrot"; 复制代码

目标是获取所有以 ing 结尾的单词,并且返回他们去掉 ing 的动词格式

  • 在字符串中搜索以“ ing”结尾的任何单词(例如“ climbing”)
  • 捕获单词中“ ing”之前的所有字母(例如“ climb”)
  • 返回字符串

为了达到目的,我们使用下面的正则:

const regex = /([a-z]*)ing/g; 复制代码

正则表达式很难,让我们对其分解,了解每一部分的原理。

  • ([a-z]*) -匹配任何连续包含字母a到z的字符串。我们将其包装在括号中 () ,以使其成为“捕获组”。顾名思义,捕获组就是“捕获”与 该特定部分 匹配的字符组。在我们的示例中,我们希望匹配所有以“ ing”结尾的单词,但是我们真正想要的是之前的字母,因此使用捕获组。
  • ing -仅匹配以“ ing”结尾的字符串。
  • /g -全局搜索。搜索整个输入字符串。不要在第一次匹配成功后就停下来。

String.prototype.match

我们希望通过正则表达式查找动词。JavaScript中的 match 函数可以传入正则表达式。

const test = "climbing, oranges, jumping, flying, carrot"; const regex = /([a-z]*)ing/g; test.match(regex); // ["climbing", "jumping", "flying"] 复制代码

但这并不是我们想要的。它返回了完整的单词,而不只是动词!发生这种情况是因为 match 不支持使用 /g 标志捕获组。 match 在不需要使用捕获组的时候很好用,但现在似乎不太适合。

RegExp.prototype.exec

exec 方法在正则表达式本身上执行,而不是在类似字符串上执行 matchexec 支持捕获组,但是使用的API稍显笨拙。您必须不断 exec 在正则表达式上反复调用以获取下一个匹配项。这要求我们创建一个无限循环并持续调用, exec 执行到没有匹配项为止。

const regex = /([a-z]*)ing/g; const matches = []; while (true) { const match = regex.exec(test); if (match === null) break; matches.push(match[1]); } matches // ["climb", "jump", "fly"] 复制代码

这种方法是可以满足需求的,但是有点混乱不清晰。这样做的主要原因有两个:

  • 仅当 /g 标志设置在末尾时,它才执行预期的操作。如果您将正则表达式作为变量或参数传递,这可能会引起混淆。
  • 使用 /g 标志时, RegExp 对象是有状态的,并存储对其最后匹配项的引用。而这一点有可能导致bug,如果你重复且不同的调用它。

使用String.prototype.matchAll

终于,我们到了。 String.prototype.matchAll 这将使我们的生活更加轻松,并提供一个简单的解决方案来支持捕获组,并返回一个可迭代的数组,该数组可以扩展为数组。让我们将上面的代码重构使用 matchAll。

const test = "climbing, oranges, jumping, flying, carrot"; const regex = /([a-z]*)ing/g; const matches = [...test.matchAll(regex)]; const result = matches.map(match => match[1]); result // ["climb", "jump", "fly"] 复制代码

我们得到一个二维数组,第一个元素中的单词全匹配**(’climbing’) ,第二个元素中的捕获组 (’climb’)**。通过迭代并保留第二个元素,终于我们得到想要的结果。

动态import()

由于webpack的支持,这是您可能已经熟悉的一种方式。并且在生产JavaScript应用程序中经常使用它来进行“ 代码拆分 ”。代码拆分在单个页面应用程序中非常强大。在许多情况下,可以大大加快初始页面加载时间。

动态导入语法允许我们将 import 作为能够返回 promise 的函数进行调用。这对于在代码运行时动态加载模块特别有用。例如,您可能想基于代码中的某些逻辑来加载某个组件或模块。

// JavaScript for side panel is loaded const sidePanel = await import("components/SidePanel"); sidePanel.open() 复制代码

还支持插值。

async function openSidePanel(type = "desktop") { // JavaScript for desktop side panel is loaded const sidePanel = await import(`components/${type}/SidePanel`); sidePanel.open(); } 复制代码

这个新功能增强了我们应用程序的性能。我们不必预先加载所有的JavaScript。动态导入使我们能够仅加载所需数量的JS控件,性能上极大提升。

BigInt

JavaScript可以处理的最大数量为 2^53 。就是这样 9007199254740991 ,或者您可以使用更好记一点的 Number.MAX_SAFE_INTEGER

当你数字超过 MAX_SAFE_INTEGER 时会发生什么?

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 - wut console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 - WUT 复制代码

BigInt 是ES2020中新增的类型,用于解决此问题。要将number或者string转换为 BigInt ,可以使用BigInt构造函数,也可以n在其末尾添加 a 。因此,要修正上面的示例,在将2加到后得到相同的值 Number.MAX_SAFE_INTEGER

BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9007199254740993n :white_check_mark: 复制代码

谁需要这些数字呢?

您可能会惊讶于在软件开发中拥有如此庞大的数字非常普遍。比如时间戳和唯一标识符就可以是这么大的数字。

例如,Twitter使用如此大的整数作为推文的唯一键。如果您尝试将JavaScript存储为不带数字的数字,则会在JavaScript应用程序中看到奇怪的错误BigInt。您将不得不使用第三方社区包,或者将它们存储为字符串。这也是JavaScript开发人员在BigInt不支持的环境中解决此问题的常用解决方案。

Promise.allSettled

假设您正在参加考试。收到结果后,您发现您正确回答了99%的问题。在大多数生活领域中,您都会充满对胜利的希望。不过,在这种情况下,您依然会在得分中收到一个大红色的叉,告诉您您失败了。

这就是 Promise.all 的工作方式。 Promise.all 接受一系列承诺,并同时获取其结果。如果它们全部成功,您的 Promise.all 成功。如果一项或多项失败,您的 Promise 就会被 reject 。在某些情况下,您可能需要这种处理方式,但事实上并不总是这样。

引入Promise.allSettled

ES2020的 Promise.allSettled 在考试方面可要比 Promise.all 好得多。它将使您轻拍一下,并告诉您不要担心1%的 reject

Promise 出现时,无论它被认为是 resolverejectPromise.allSettled 允许我们传递一系列的 Promise ,这些 Promise 将在全部结束后, Promise 的返回值是一个装满 Promise 结果的数组。让我们看一个例子:

const promises = [ fetch('/api1'), fetch('/api2'), fetch('/api3'), ]; Promise.allSettled(promises). then((results) => results.forEach((result) => console.log(result.status))); // "fulfilled" // "fulfilled" // "rejected" 复制代码

globalThis

globalThis 是一个全新的标准方法用来获取全局 this 。之前开发者会通过如下的一些方法获取:

  • 全局变量 window:是一个经典的获取全局对象的方法。但是它在 Node.js 和 Web Workers 中并不能使用
  • 全局变量 self:通常只在 Web Workers 和浏览器中生效。但是它不支持 Node.js。一些人会通过判断 self – 是否存在识别代码是否运行在 Web Workers 和浏览器中
  • 全局变量 global:只在 Node.js 中生效

由于Js的通用性,相同的JavaScript代码可以在NodeJS的客户端和服务器上运行。这提出了一系列特殊的挑战。

一个是全局对象,可以从任何运行的代码段中访问它。 window 在浏览器中,但 globalNode 中。编写访问此全局对象的通用代码依赖于一些条件逻辑,这些条件逻辑可能看起来像这样(蒙住眼睛)。

(typeof window !== "undefined" ? window : (typeof process === 'object' && typeof require === 'function' && typeof global === 'object') ? global : this); 复制代码

值得庆幸的是,ES2020带来了 globalThis 全局变量。现在可以放松的不去考虑 windowglobal 而统一前端或后端代码。

globalThis.something = "Hello"; // Works in Browser and Node. 复制代码

for-in的学问

for (x in obj) ... 是在很多时候都超级有用的语法,主要是遍历对象的key值。

for (let key in obj) { console.log(key); } 复制代码

该提议与在循环中迭代元素的顺序和语义有关。在提出这个之前,大多数JavaScript引擎已经应用了常识,所有主流浏览器都按照定义它们的顺序遍历对象的属性。但是,有些细微差别。这些主要涉及更高级的功能,例如代理。for..in循环语义从一开始就没有包含在JavaScript规范中,但是该提议可确保每个人在for..in工作方式上都具有一致的参考点。

可选链操作符(Optional Chaining)

可选链接可能是相当长一段时间以来JavaScript中最受期待的功能之一。在对更干净的JavaScript代码的影响方面,这一得分非常高!!!

当检查嵌套对象内部的属性时,通常必须检查中间对象的存在。让我们来看一个例子:

const test = { name: "foo", age: 25, address: { number: 44, street: "Sesame Street", city: { name: "Fake City", lat: 40, lon: 74 } } } // when we want to check for the name of the city if (test.address.city.name) { console.log("City name exists!"); } // City Name exists! 复制代码

这是一个成功的例子!但是在开发中,不能总是依靠幸福的曙光照耀我们。有时中间的值会不存在。让我们看同样的例子,但是没有给 city 定义值。

const test = { name: "foo", age: 25, address: { number: 44, street: "Sesame Street" } } if (test.address.city.name) { console.log("City name exists!"); } // TypeError: Cannot read property 'name' of undefined 复制代码

我们的代码已经报错了。这是因为我们试图访问 nametest.address.city ,但是这是一个 undefined 。当尝试读取 undefined 上的属性时 TypeError 肯定会抛出以上内容。那我们该如何解决?在之前许多JavaScript代码中,您将看到以下解决方案:

const test = { name: "foo", age: 25, address: { number: 44, street: "Sesame Street" }, } if (test.address && test.address.city && test.address.city.name) { console.log("City name exists!"); } // no TypeError thrown! 复制代码

我们的代码现在可以运行了,但是我们不得不在 出bug 那儿写很多代码来解决问题。我们按理来说可以做得更好! ES2020 的可选链接运算符使您可以使用新的 ? 语法检查对象深处是否存在值 。这是使用可选链接运算符重写的上述示例:

const test = { name: "foo", age: 25, address: { number: 44, street: "Sesame Street" }, } // much cleaner. if (test?.address?.city?.name) { console.log("City name exists!"); } // no TypeError thrown! 复制代码

看起来不错。我们将十分长的 && 链,浓缩为更加简洁易读的可选链运算符。如果链中的任何值是 null 或者 undefined ,则表达式仅返回 undefined

可选的链接运算符非常强大。请看以下示例可以使用它的其他方式:

const nestedProp = obj?.['prop' + 'Name']; // computed properties const result = obj.customMethod?.(); // functions const arrayItem = arr?.[42]; // arrays 复制代码

空位合并运算符(Nullish coalescing Operator)

空位合并运算符是一个非常简单的名称,听起来很花哨。此功能使我们能够检查一个值是否为 nullundefined ,如果是,则默认为另一个值,仅此而已。

为什么这有用?让我们假设一下,你的JavaScript中有五个虚值。

  • null
  • undefined
  • 空字符串 “”
  • 没有数字-NaN

我们可能有一些要检查数值的代码。我们想为球队中的球员分配一个小队号码。如果他们已经有小队号码,我们将保留该号码。否则,我给他们一个叫”unssigned”的值。

const person = { name: "John", age: 20, squadNumber: 100 }; const squadNumber = person.squadNumber || "unassigned"; console.log(`${person.name}s squad number is ${squadNumber}`); // "Johns squad number is 100" 复制代码

此代码可以正常工作。但是,让我们从一个稍微不同的角度来考虑。如果我们的 person0 ,该怎么办?

const person = { name: "Dave", age: 30, squadNumber: 0 }; const squadNumber = person.squadNumber || "unassigned"; console.log(`${person.name}s squad number is ${squadNumber}`); // "Daves squad number is unassigned" 复制代码

这是不对的。 Dave 已经为球队效力了多年。我们的代码有一个错误。发生这种情况是因为 0 ,导致我们 false 条件的 || 被调用。在此示例中,对结果值的检查存在问题。您当然可以通过以下操作解决此问题:

const person = { name: "Dave", age: 30, squadNumber: 0 }; const squadNumber = person.squadNumber >= 0 ? person.squadNumber : "unassigned"; console.log(`${person.name}s squad number is ${squadNumber}`); // "Daves squad number is 0" 复制代码

这是一个还可以的解决方案,但我们可以使用空位合并运算符来解决。

运算符 ?? 可以更好地确保我们的值是 nullundefined

const person = { name: "Dave", age: 30, squadNumber: 0 }; // Nullish Coalescing Operator // If person.squadNumber is null or undefined // set squadNumber to unassigned const squadNumber = person.squadNumber ?? "unassigned"; console.log(`${person.name}s squad number is ${squadNumber}`); // "Daves squad number is 0" 复制代码

import.meta

import.meta 是一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。

如果您熟悉Node,则可以通过 __dirname__filename 属性与 CommonJS 一起使用此功能。

const fs = require("fs"); const path = require("path"); // resolves data.bin relative to the directory of this module const bytes = fs.readFileSync(path.resolve(__dirname, "data.bin")); 复制代码

那浏览器呢?这里import.meta将会变得有用。如果要从浏览器中运行的JavaScript模块导入相对路径,则可以让 import.meta 这样做:

// Will import cool-image relative to where this module is running. const response = await fetch(new URL("../cool-image.jpg", import.meta.url)); 复制代码

此功能对开发第三方库的作者那可是非常有用了,因为他们不知道他们的代码将在什么地方以什么方式运行的。

结论

总而言之,ECMAScript规范中添加的最新功能为不断发展和发展的JavaScript生态系统增加了更多实用性,灵活性和强大功能。看到社区继续以如此快的速度蓬勃发展和进步,真的令人鼓舞和激动。

您可能在想:“这些听起来不错,但是我如何在我的项目里使用ES2020功能呢?

什么时候,如何使用这些东西?

现在就可以使用它!在大多数现代浏览器和Node的最新版本中,支持所有的功能。 caniuse.com 是另一个很棒的网站,可用于检查跨浏览器和Node的 ES2020 新增功能的兼容性级别。

如果需要在较旧的浏览器或Node版本上使用这些功能,则需要 babel / typescript


喜欢 (0)