📜 ⬆️ ⬇️

JavaScript Guide, Part 9: ES7, ES8, and ES9 Features Overview

Today, in the ninth part of the translation of the JavaScript manual, we will review the features that have emerged in the language thanks to the standards ES7, ES8 and ES9.

→ Part 1: first program, language features, standards
→ Part 2: code style and program structure
→ Part 3: Variables, Data Types, Expressions, Objects
→ Part 4: Functions
→ Part 5: Arrays and Loops
→ Part 6: Exceptions, Semicolon, Pattern Literals
→ Part 7: strict mode, this keyword, events, modules, mathematical calculations
→ Part 8: ES6 feature overview
→ Part 9: Overview of ES7, ES8, and ES9 Capabilities



ES7 standard


The ES7 standard, which, in accordance with the official terminology, is called ES2016, was released in the summer of 2016. In comparison with ES6, it brought not so much new to the language. In particular, we are talking about the following:
')

ArArray.prototype.includes () method


The Array.prototype.includes() method is designed to check for the presence of an element in an array. Finding the required in the array, it returns true , not finding - false . Prior to ES7, the indexOf() method served to perform the same operation, which returns, if an element is found, the first index by which it can be found in the array. If indexOf() does not find the element, it returns the number -1 .

According to the JavaScript type conversion rules, the number -1 converted to true . As a result, to check the results of the work of indexOf() it was necessary to use a not very convenient construction of the following form.

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

If in a situation like this, assuming that indexOf() does not find an element, returns false , use something like the one shown below, the code will not work correctly.

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

In this case, it turns out that the construction ![1,2].indexOf(3) gives false .

Using the includes() method, such comparisons look much more logical.

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

In this case, the construction [1,2].includes(3) returns false , this value is an operator ! turns into true and a message is received in the console stating that the required element in the array was not found.

â–ŤExponentiation operator


The exponentiation operator performs the same function as the Math.pow() method, but it is more convenient to use it than the library function, since it is part of the language.

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

This operator can be considered a nice addition JS, which is useful in applications that perform certain calculations. A similar operator exists in other programming languages.

ES8 standard


Standard ES8 (ES2017) was released in 2017. He, like ES7, introduced not so much new to the language. Namely, we are talking about the following possibilities:


â–ŤAdd lines up to specified length


In ES8, there are two new methods for the String object — padStart() and padEnd() .

The padStart() method fills the current line with another line until the final line reaches the desired length. Filling occurs at the beginning of the line (left). Here is how to use this method.

 str.padStart(targetLength [, padString]) 

Here str is the current string, targetLength is the length of the resulting string (if it is less than the length of the current string — this string will be returned unchanged), padString — optional — the string used to populate the current string. If the padString parameter padString not specified, a space character is used to padString current line to the specified length.

The padEnd() method is similar to padStart() , but the line is filled to the right.

Consider examples of the use of these methods.

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

Here, when using padStart() indicating only the desired length of the result line, spaces were added to the beginning of the source line. When using padEnd() * characters were added to the end of the source line with the indication of the length of the final line and the line.

Object Method Object.values ​​()


This method returns an array containing the values ​​of the object's own properties, that is, properties that the object itself contains, and not those that are accessible to it through a chain of prototypes.

Here's how to use it.

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

This method also applies to arrays.

Object Method Object.entries ()


This method returns an array, each element of which is also an array containing, in the format of [key, value] , the keys and values ​​of the object's own properties.

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

When applying this method to arrays, indexes of elements are displayed as keys, and what is stored in the array at the corresponding indices is displayed as values.

Get Method getOwnPropertyDescriptors ()


This method returns information about all the object's own properties. Associated attribute sets (descriptors) are associated with properties of objects. In particular, we are talking about the following attributes:


Here is how to use this method.

 Object.getOwnPropertyDescriptors(obj) 

It takes an object whose property information you want to know, and returns an object containing this information.

 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 } } */ 

Why do you need this method? The fact is that it allows you to create small copies of objects, copying, among other properties, getters and setters. This could not be done using the Object.assign() method for copying objects, which appeared in the ES6 standard.

In the following example, there is an object with a setter that outputs, using console.log() , what it is trying to write to its corresponding property.

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

Let's try to copy this object using the assign() method.

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

As you can see, this approach does not work. The name property, which in the original object was a setter, is now represented as a regular property.

Now we will copy the object using the Object.defineProperties() methods (it appeared in ES5.1) and Object.getOwnPropertyDescriptors() .

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

Here in the copy of the object the setter remained.

It should be noted that the limitations specific to Object.assign() are also characteristic of the Object.create() method when it is used to clone objects.

â–ŤCompleted commas in function parameters


This feature allows you to leave a comma at the end of the list of parameters or arguments, respectively, when you declare and when you call functions.

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

This improves the usability of version control systems. Namely, we are talking about the fact that, when adding new parameters to a function, it is not necessary to change the existing code just to insert a comma.

â–Ť Asynchronous functions


The ES2017 standard introduced the async/await construction, which can be considered the most important innovation of this version of the language.

Asynchronous functions are a combination of promises and generators; they simplify constructions, which were previously described with a large amount of template code and inconvenient promis chains. In fact, this is a high-level abstraction over promises.

When promises appeared in the ES2015 standard, they were designed to solve existing problems with asynchronous code, which they did. But in the two years that have shared the ES2015 and ES2017 standards, it has become clear that promises cannot be considered the final solution to these problems.

In particular, promises were aimed at solving the problem of “hell callbacks”, but, having solved this problem, they themselves showed themselves not with the best hand due to the complexity of the code in which they are used. As a matter of fact, the async/await construction solves the problem of promises and improves usability with asynchronous code.

Consider an example.

 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') 

This code will display the following in the console.

 Before After I did something 

As you can see, after the call to doSomething() program continues to run, after Before , After is immediately output to the console, and after three seconds has passed, I did something is displayed.

Asynchronous function sequential call


If necessary, asynchronous functions can form something like call chains. Such constructions have better readability than something similar, based solely on promises. This can be seen in the following example.

 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 }) 

â–ŤShared memory and atomic operations


Here we are talking about the SharedArrayBuffer object, which allows us to describe shared memory areas, and the Atomics object, which contains a set of atomic operations in the form of static methods. Details about the possibilities that these objects give to the programmer can be found here .

ES9 standard


ES9 (ES2018) is the latest version of the standard at the time of publication of this material. Here are its main features:


â–ŤApplication of spread and rest operators to objects


We have already spoken about the rest and spread operators, which appeared in ES6 and can be used to work with arrays. Both of them look like three points. The rest operator, in the following example of destructuring an array, allows you to place its first and second elements in the first and second constants, and all the others in the others constant.

 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 ] 

The spread operator allows you to pass arrays to functions that are waiting for normal parameter lists.

 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 

Now, using the same approach, you can work with objects. Here is an example of using the rest operator in a destructive assignment operation.

 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 } 

Here is the spread operator used when creating a new object based on an existing one. This example continues the previous one.

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

â–Ť Asynchronous iterators


The new for-await-of design allows you to call asynchronous functions that return promises in cycles. Such cycles are waiting for promise resolution before proceeding to the next step. Here's what it looks like.

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

It should be noted that such cycles should be used in asynchronous functions - just as is done when working with the async/await construction.

Prom Method Promise.prototype.finally ()


If the promise is successfully resolved, the next then() method is called. If something goes wrong, the catch() method is called. The finally() method allows you to execute some code, regardless of what happened before.

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

Improved regular expressions


In regular expressions, it was possible to retrospectively check strings ( ?<= ). This allows you to search in the lines for some constructions, in front of which there are some other constructions.

The possibility of forward checks using the ?= Construct was in regular expressions implemented in JavaScript and up to the ES2018 standard. Such checks let you know if another fragment follows a fragment of a string.

 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 

The design ?! performs the inverse operation — a match will be found only if there is no other line following the specified string.

 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 

For retrospective verification, as already mentioned, the ?<= Construct is used.

 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 

The inverse operation can be performed using the ?<! .

 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 

Regular Expression Unicode Control Sequences


In regular expressions, you can use the class \d , the corresponding any digit, the class \s , the corresponding any space character, the class \w , which matches any alphanumeric character, and so on. The feature in question extends the set of classes that can be used in regular expressions, allowing you to work with Unicode sequences. We are talking about the class \p{} and the opposite class \P{} .

In Unicode, each character has a set of properties. These properties are specified in curly brackets of the group \p{} . So, for example, the Script property determines the family of languages ​​to which a character belongs, the ASCII property, logical, is true for ASCII characters, and so on. For example, find out if certain strings contain exclusively ASCII characters.

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

The ASCII_Hex_Digit property is true only for characters that can be used to write hexadecimal numbers.

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

There are many other similar properties that are used the same way as described above. Among them - Uppercase , Lowercase , White_Space , Alphabetic , Emoji .

Here, for example, using the Script property to determine which alphabet is used in a string. Here we check the string for use of the Greek alphabet.

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

Details on these properties can be read here .

Named groups


Captured character groups in ES2018 can be given names. Here's what it looks like.

 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' } ] */ 

Without the use of named groups, the same data would be available only as elements of an array.

 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 ] */ 

Regular Expression Flag s


Using the s flag causes the symbol to be . (dot) will, among other things, match the newline character. Without the use of this flag, a dot matches any character except a newline character.

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

Results


With this material we complete the publication of translations of this JavaScript manual. We hope these publications have helped those who have not worked with JavaScript before, to take their first steps in programming in this language.

Dear readers! If you have not written in JS before and mastered this language according to this manual, please share your impressions.

Source: https://habr.com/ru/post/431872/


All Articles