JavaScript指南第9部分:ES7,ES8和ES9标准概述

今天,在JavaScript手册翻译的第九部分中,将概述由于ES7,ES8和ES9标准而在语言中出现的功能。

第1部分:第一个程序,语言功能,标准
第2部分:代码样式和程序结构
第3部分:变量,数据类型,表达式,对象
第4部分:功能
第5部分:数组和循环
第6部分:异常,分号,通配符文字
第7部分:严格模式,此关键字,事件,模块,数学计算
第8部分:ES6功能概述
第9部分:ES7,ES8和ES9标准概述



ES7标准


根据官方术语,称为ES2016的ES7标准于2016年夏天发布。 与ES6相比,他带来的语言并不新鲜。 特别是,我们在谈论以下内容:

  • Array.prototype.includes()方法。
  • 求幂运算符。

▍方法Array.prototype.includes()


Array.prototype.includes()方法旨在检查数组中是否存在元素。 在数组中找到所需的对象,它返回true ,而不是find- false 。 在ES7之前, indexOf()方法用于执行相同的操作,如果找到元素,则该方法返回在数组中可以找到该元素的第一个索引。 如果indexOf()找不到元素,则返回数字-1

根据JavaScript类型转换规则,数字-1转换为true 。 结果,要检查indexOf()的运算结果indexOf()应该使用以下形式的不太方便的构造。

 if ([1,2].indexOf(3) === -1) { console.log('Not found') } 

如果在类似情况下,假设没有找到任何元素的indexOf()返回false ,则使用如下所示的代码,则代码将无法正常工作。

 if (![1,2].indexOf(3)) { // console.log('Not found') } 

在这种情况下,事实证明![1,2].indexOf(3)的构造为false

使用includes()方法,这种比较看起来更加合乎逻辑。

 if (![1,2].includes(3)) { console.log('Not found') } 

在这种情况下,构造[1,2].includes(3)返回false ,此值是一个运算符! 变为true ,控制台将收到一条消息,指出未找到阵列中的项目。

▍求幂运算符


幂运算符执行与Math.pow()方法相同的功能,但是比起库函数使用它更方便,因为它是语言的一部分。

 Math.pow(4, 2) == 4 ** 2 //true 

该运算符可以被视为JS的一个令人愉快的添加,它在执行某些计算的应用程序中很有用。 其他编程语言中也存在类似的运算符。

ES8标准


ES8标准(ES2017)于2017年发布。 像ES7一样,他并没有给语言带来太多帮助。 即,我们正在谈论以下功能:

  • 将字符串添加到给定的长度。
  • 方法Object.values()
  • 方法Object.entries()
  • 方法Object.getOwnPropertyDescriptors()
  • 函数参数中的尾部逗号。
  • 异步功能。
  • 使用共享内存和原子操作。

lines将线添加到给定的长度


ES8引入了两个新的String对象方法padStart()padEnd()

padStart()方法用另一行填充当前行,直到最后一行达到所需的长度。 填充发生在行的开头(左)。 这是使用此方法的方法。

 str.padStart(targetLength [, padString]) 

这里的str是当前行, targetLength是最后一行的长度(如果小于当前行的长度,则将返回此行而不会更改), padString是一个可选参数-用于填充当前行的行。 如果未指定padString ,则使用空格字符padString当前行padString到指定的长度。

padEnd()方法类似于padStart() ,但该行在右侧填充。

考虑使用这些方法的示例。

 const str = 'test'.padStart(10) const str1 = 'test'.padEnd(10,'*') console.log(`'${str}'`) //'      test' console.log(`'${str1}'`) //'test******' 

在这里,当仅使用padStart()并使用所需长度的结果字符串时, padStart()空格添加到原始字符串的开头。 当使用padEnd()以及最后一行的长度和要填充它的行时, *字符被添加到原始行的末尾。

▍方法Object.values()


此方法返回一个数组,其中包含对象自己的属性的值,即对象本身包含的那些属性,而不是可通过原型链访问的那些属性。

这是使用方法。

 const person = { name: 'Fred', age: 87 } const personValues = Object.values(person) console.log(personValues) // ['Fred', 87] 

此方法也适用于数组。

▍方法Object.entries()


此方法返回一个数组,该数组的每个元素也是一个数组,该数组包含[key, value]格式的对象自身属性的键和值。

 const person = { name: 'Fred', age: 87 } const personValues = Object.entries(person) console.log(personValues) // [['name', 'Fred'], ['age', 87]] 

将这种方法应用于数组时,元素的索引显示为键,而数组中对应索引处存储的内容则显示为值。

▍getOwnPropertyDescriptors()方法


此方法返回有关对象自身所有属性的信息。 属性集(描述符)与对象属性相关联。 特别是,我们在谈论以下属性:

  • value对象属性的值。
  • writable -如果可以更改属性,则包含true
  • get包含与属性关联的getter函数,或者,如果没有这样的函数,则为undefined
  • set-包含属性的setter函数或undefined
  • configurable -如果为false无法删除该属性,除了值之外,不能更改其属性。
  • enumerable -如果此属性包含true,则该属性可枚举。

这是使用此方法的方法。

 Object.getOwnPropertyDescriptors(obj) 

它获取需要查找其属性信息的对象,然后返回包含此信息的对象。

 const person = { name: 'Fred', age: 87 } const propDescr = Object.getOwnPropertyDescriptors(person) console.log(propDescr) /* { name:  { value: 'Fred',    writable: true,    enumerable: true,    configurable: true }, age:  { value: 87,    writable: true,    enumerable: true,    configurable: true } } */ 

为什么需要这种方法? 事实是,除了其他属性,getter和setter之外,它还允许您创建对象的小型副本,进行复制。 无法使用ES6标准中出现的Object.assign()方法来复制对象。

下面的示例包含一个带有setter的对象,该对象使用console.log()显示其试图写入其相应属性的内容。

 const person1 = { set name(newName) {     console.log(newName) } } person1.name = 'x' // x 

让我们尝试使用assign()方法复制该对象。

 const person2 = {} Object.assign(person2, person1) person2.name = 'x' //     ,    

如您所见,这种方法行不通。 name属性(它是原始对象中的设置器)现在表示为常规属性。

现在,我们将使用Object.defineProperties() (在ES5.1中出现)和Object.getOwnPropertyDescriptors()方法复制对象。

 const person3 = {} Object.defineProperties(person3, Object.getOwnPropertyDescriptors(person1)) person3.name = 'x' //x 

在此,设置器保留在对象的副本中。

应该注意的是,当用于克隆对象时,特定于Object.assign()的限制也是Object.create()方法的特征。

function功能参数的补逗号


此功能使您可以在声明和调用函数时在参数或参数列表的末尾留下逗号。

 const doSomething = ( var1, var2, ) => { //... } doSomething( 'test1', 'test2', ) 

这提高了版本控制系统的可用性。 即,我们正在谈论的事实是,在向函数添加新参数时,不必为了插入逗号而更改现有代码。

▍异步功能


async/await构造已出现在ES2017标准中,可以认为是该版本语言的最重要创新。

异步功能是promise和生成器的组合;它们简化了以前需要大量模板代码和不方便描述promise链的构造。 实际上,我们正在谈论对承诺的高级抽象。

当诺言出现在ES2015标准中时,它们旨在解决异步代码存在的问题。 但是在共享ES2015和ES2017标准的两年中,很明显,不能将承诺视为解决这些问题的最终解决方案。

特别是,这些承诺旨在解决“回调地狱”问题,但是,解决了这个问题后,由于使用它们的代码的复杂性,它们本身并没有表现出最好的一面。 实际上, async/await构造解决了promise问题并提高了异步代码的可用性。

考虑一个例子。

 function doSomethingAsync() { return new Promise((resolve) => {     setTimeout(() => resolve('I did something'), 3000) }) } async function doSomething() { console.log(await doSomethingAsync()) } console.log('Before') doSomething() console.log('After') 

此代码会将以下内容输出到控制台。

 Before After I did something 

如您所见,在调用doSomething()程序继续运行,在控制台中显示BeforeAfter ,并且经过三秒钟之后, I did something

串行异步函数调用


如有必要,异步函数可以形成类似于调用链的内容。 仅基于承诺,此类设计的可读性优于类似设计。 在下面的示例中可以看到。

 function promiseToDoSomething() { return new Promise((resolve)=>{     setTimeout(() => resolve('I did something'), 10000) }) } async function watchOverSomeoneDoingSomething() { const something = await promiseToDoSomething() return something + ' and I watched' } async function watchOverSomeoneWatchingSomeoneDoingSomething() { const something = await watchOverSomeoneDoingSomething() return something + ' and I watched as well' } watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => { console.log(res) // I did something and I watched and I watched as well }) 

▍共享内存和原子操作


在这里,我们谈论的是SharedArrayBuffer对象,它允许您描述共享内存区域;还有Atomics对象,它包含一组以静态方法形式进行的原子操作。 有关这些对象为程序员提供可能性的详细信息,请参见此处

ES9标准


ES9(ES2018)是发布此材料时该标准的最新版本。 其主要功能如下:

  • 将传播和休息声明应用于对象。
  • 异步迭代器。
  • 方法Promise.prototype.finally()
  • 正则表达式增强。

spread散布和休息操作员在对象上的应用


我们已经讨论过ES6中出现的其余和散布运算符,这些运算符可用于处理数组。 他们看起来都像三个点。 在下面的解构数组示例中,rest运算符使您可以将其第一个和第二个元素放在常量firstsecond ,并将所有其余元素放在常量others

 const numbers = [1, 2, 3, 4, 5] const [first, second, ...others] = numbers console.log(first) //1 console.log(second) //2 console.log(others) //[ 3, 4, 5 ] 

spread运算符使您可以将数组传递给需要常规参数列表的函数。

 const numbers = [1, 2, 3, 4, 5] const sum = (a, b, c, d, e) => a + b + c + d + e const res = sum(...numbers) console.log(res) //15 

现在,使用相同的方法,您可以处理对象。 这是在破坏性分配操作中使用rest语句的示例。

 const { first, second, ...others } = { first: 1, second: 2, third: 3, fourth: 4, fifth: 5 } console.log(first) //1 console.log(second) //2 console.log(others) //{ third: 3, fourth: 4, fifth: 5 } 

这是在基于现有对象创建新对象时使用的传播语句。 本示例继续上一个示例。

 const items = { first, second, ...others } console.log(items) //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 } 

▍异步迭代器


新的for-await-of构造允许您调用异步函数,这些异步函数在循环中返回promise。 这样的循环等待承诺的解决,然后再进行下一步。 这是它的外观。

 for await (const line of readLines(filePath)) { console.log(line) } 

同时,应注意,此类循环应在异步函数中使用-与使用async/await构造时相同的方式。

▍Promise.prototype.finally()方法


如果成功解决了诺言,则调用下一个then()方法。 如果出现问题,将调用catch()方法。 finally()方法允许您执行某些代码,而不管之前发生了什么。

 fetch('file.json') .then(data => data.json()) .catch(error => console.error(error)) .finally(() => console.log('finished')) 

▍正则表达式的改进


正则表达式具有追溯检查字符串( ?<= )的能力。 这使您可以在行中搜索某些构造之前,还有其他一些构造。

在ES2018标准之前,使用JavaScript实现的正则表达式具有使用?=构造进行检查的能力。 通过此类检查,您可以知道另一个片段是否跟随一行的某个片段。

 const r = /Roger(?= Waters)/ const res1 = r.test('Roger is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1) //false console.log(res2) //true 

施工?! 执行相反的操作-仅当另一行不在给定行之后,才会找到匹配项。

 const r = /Roger(?! Waters)/g const res1 = r.test('Roger is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1) //true console.log(res2) //false 

在回顾性验证中,如前所述,使用结构?<=

 const r = /(?<=Roger) Waters/ const res1 = r.test('Pink Waters is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1) //false console.log(res2) //true 

与所述相反的操作可以使用结构?<!

 const r = /(?<!Roger) Waters/ const res1 = r.test('Pink Waters is my dog') const res2 = r.test('Roger is my dog and Roger Waters is a famous musician') console.log(res1) //true console.log(res2) //false 

Unicode正则转义序列


在正则表达式中,可以使用与任何数字匹配的\d类,与任何空格字符匹配的\s类,与任何字母数字字符匹配的\w类,等等。 有问题的功能扩展了可在正则表达式中使用的类集,从而使您可以处理Unicode序列。 我们正在谈论类\p{}和类\P{}的逆。

在Unicode中,每个字符都有一组属性。 这些属性显示在\p{}组的花括号中。 因此,例如,“ Script属性确定字符所属的语言家族,“ ASCII属性为逻辑,ASCII字符为true ,依此类推。 例如,我们将找出某些行是否仅包含ASCII字符。

 console.log(r.test('abc')) //true console.log(r.test('ABC@')) //true console.log(r.test('ABC')) //false 

ASCII_Hex_Digit属性仅对于可用于写入十六进制数字的字符为true

 const r = /^\p{ASCII_Hex_Digit}+$/u console.log(r.test('0123456789ABCDEF')) //true console.log(r.test('H')) //false 

如上所述,还有许多其他类似的属性以相同的方式使用。 其中包括UppercaseLowercaseWhite_SpaceAlphabeticEmoji

例如,这是使用Script属性确定字符串中使用哪个字母的方法。 在这里,我们检查字符串是否使用希腊字母。

 const r = /^\p{Script=Greek}+$/u console.log(r.test('ελληνικά')) //true console.log(r.test('hey')) //false 

这些属性的详细信息可以在这里找到。

命名组


可以为ES2018中捕获的字符组命名。 这是它的外观。

 const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/ const result = re.exec('2015-01-02') console.log(result) /* [ '2015-01-02', '2015', '01', '02', index: 0, input: '2015-01-02', groups: { year: '2015', month: '01', day: '02' } ] */ 

如果不使用命名组,则相同的数据仅可用作数组元素。

 const re = /(\d{4})-(\d{2})-(\d{2})/ const result = re.exec('2015-01-02') console.log(result) /* [ '2015-01-02', '2015', '01', '02', index: 0, input: '2015-01-02', groups: undefined ] */ 

正则表达式标志


使用s标志生成一个字符. (点)将与换行符匹配。 没有此标志,句点匹配换行符以外的任何字符。

 console.log(/hi.welcome/.test('hi\nwelcome')) // false console.log(/hi.welcome/s.test('hi\nwelcome')) // true 

总结


借助这些材料,我们将完成 JavaScript手册的翻译版本。 我们希望这些出版物能帮助那些以前没有使用JavaScript的人迈出用这种语言编程的第一步。

亲爱的读者们! 如果您以前没有用JS编写过并且在本指南中已经熟练掌握了该语言,请分享您的印象。

Source: https://habr.com/ru/post/zh-CN431872/


All Articles