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

Original author: Flavio Copes
  • Transfer
  • Tutorial
Today, in the ninth part of the translation of the JavaScript guide, 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 cycles
Part 6: exceptions, semicolon, template literals
Part 7: strict mode, this keyword, events, modules, mathematical calculations
Part 8: an overview of the capabilities of the ES6 standard
Part 9: an overview of the capabilities of the ES7, ES8, and ES9 standards

ES7 standard

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

  • Method Array.prototype.includes().
  • Exponentiation operator.

ArArray.prototype.includes () method

The method is Array.prototype.includes()designed to check the presence of an element in the array. Finding the desired in the array, it returns true, not finding - false. Prior to ES7 indexOf(), a 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 the indexOf()item does not find it, it returns a number -1.

In accordance with the JavaScript type conversion rules, the number is -1converted to true. As a result, to verify the results of the work, indexOf()it was necessary to use a not very convenient construction of the following type.

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

If in a similar situation, assuming that indexOf(), without finding an element, returns false, to 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 design ![1,2].indexOf(3)gives false.

Using the method of includes()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, the operator !converts this value into, trueand 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 method Math.pow(), 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:

  • Supplement lines to a given length.
  • Method Object.values().
  • Method Object.entries().
  • Method Object.getOwnPropertyDescriptors().
  • Trailing commas in function parameters.
  • Asynchronous functions.
  • Shared memory and atomic operations.

▍Add lines up to a given length

Two new object methods appeared in ES8 String- padStart()and padEnd().

The method padStart()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 stris the current line, targetLengthis the length of the final line (if it is less than the length of the current line - this line will be returned unchanged), padString- optional parameter - the line used to fill the current line. If the parameter is padStringnot specified, the space character is used to add the current line to the specified length.

The method is padEnd()similar 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 used padStart()with only the desired length of the result line, spaces were added to the beginning of the source line. When used padEnd()with the indication of the length of the final line and the line, characters were added to the end of the original line to fill it *.

Object Method Object.values ​​()

This method returns an array containing the values ​​of the object's own properties, that is, those 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 format [key, value], 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, the 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 properties of an object. Associated with attributes of objects are sets of attributes (descriptors). In particular, we are talking about the following attributes:

  • value - the value of the object property.
  • writable- contains trueif the property can be changed.
  • get- contains the getter function associated with the property, or if there is no such function - undefined.
  • set- contains the setter function for the property or undefined.
  • configurable- if there is false- the property cannot be deleted, its attributes cannot be changed except for the value.
  • enumerable- if this property contains true - свойствоis enumerable.

Here is how to use this method.


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)
{ name:
   { value: 'Fred',
     writable: true,
     enumerable: true,
     configurable: true },
   { 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 method for copying objects Object.assign(), which appeared in the ES6 standard.

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

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

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

const person2 = {}
Object.assign(person2, person1) = 'x'// в консоль ничего не попадает, сеттер не скопирован

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

Now let's copy the object using the methods Object.defineProperties()(it appeared in ES5.1) and Object.getOwnPropertyDescriptors().

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

Here in the copy of the object the setter remained.

It should be noted that the limitations characteristic of Object.assign()are also characteristic of the method Object.create()when it is used for cloning 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 = (
) => {

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 a design async/awaitthat 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, for which a large amount of template code and inconvenient promises chains were required to describe them. 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 showed themselves not with the best hand due to the complication of the code in which they are used. As a matter of fact, the design async/awaitsolves the problem of promises and increases the convenience of working with asynchronous code.

Consider an example.

functiondoSomethingAsync() {
  returnnewPromise((resolve) => {
      setTimeout(() => resolve('I did something'), 3000)
asyncfunctiondoSomething() {
  console.log(await doSomethingAsync())

This code will display the following in the console.

I did something

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

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.

functionpromiseToDoSomething() {
      setTimeout(() => resolve('I did something'), 10000)
asyncfunctionwatchOverSomeoneDoingSomething() {
  const something = await promiseToDoSomething()
  return something + ' and I watched'
asyncfunctionwatchOverSomeoneWatchingSomeoneDoingSomething() {
  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.
  • Asynchronous iterators.
  • Method Promise.prototype.finally().
  • Regular Expression Improvements

▍Application of spread and rest operators to objects

We have already spoken about the rest and spread operators that 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 into constants firstand second, and all the others into a constant others.

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

The operator spreadallows you to pass arrays to functions that expect 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) //1console.log(second) //2console.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 design for-await-ofallows 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.

forawait (const line of readLines(filePath)) {

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

Prom Method Promise.prototype.finally ()

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

  .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 advanced checks using a construct ?=was available 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) //falseconsole.log(res2) //true

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

const r = /Roger(?! Waters)/gconst 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) //trueconsole.log(res2) //false

In retrospective verification, as already mentioned, the construction 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) //falseconsole.log(res2) //true

The inverse operation described can be performed with the help of the construction ?<!.

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) //trueconsole.log(res2) //false

Regular Expression Unicode Control Sequences

In regular expressions, you can use a class \dthat matches any digit, a class \sthat matches any space character, a class \wthat 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 of his 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, a property Scriptdefines a family of languages ​​to which a symbol belongs, a property ASCII, a logical one, takes the valuetruefor ASCII characters, and so on. For example, find out if certain strings contain exclusively ASCII characters.

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

The property ASCII_Hex_Digitaccepts value trueonly for characters that can be used to write hexadecimal numbers.

const r = /^\p{ASCII_Hex_Digit}+$/uconsole.log(r.test('0123456789ABCDEF')) //trueconsole.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, how to use the property to Scriptdetermine which alphabet is used in a string. Here we check the string for the use of the Greek alphabet.

const r = /^\p{Script=Greek}+$/uconsole.log(r.test('ελληνικά')) //trueconsole.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')
[ '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')
[ '2015-01-02',
  index: 0,
  input: '2015-01-02',
  groups: undefined ]

Regular expression flag s

The use of the flag sleads to the fact that the symbol .(dot) will, among others, correspond to the new line character. Without the use of this flag, a dot matches any character except a newline character.

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


With this material, we complete the publication of translations of this JavaScript guide. Hopefully, 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 in this manual, please share your impressions.

Also popular now: