# JavaScript innovations: Google I / O 2019 results. Part 2

Today we are publishing the second part of the translation of JavaScript innovations. Here we talk about separators of digits of numbers, about BigInt-numbers, about working with arrays and objects, about `globalThis` , about sorting, about the internationalization API and about promises.

The first part

## Number Separators

Long numbers found in programs are hard to read. For example, `1000000000` is one billion in decimal. But at a glance it’s hard to understand. Therefore, if the reader of the program encounters something similar - he, in order to correctly perceive it, will have to carefully consider zeros.

In modern JavaScript, you can use the separator of digits of numbers - the underscore ( `_` ), the use of which improves the readability of long numbers. Here's how numbers written using a delimiter look in code:

` `var billion = 1_000_000_000; console.log( billion ); // 1000000000` `

Separators can be used for arbitrary division of numbers into fragments. JavaScript, when dealing with numbers, simply ignores separators. They can be used when writing any numbers: integers, floating point, binary, hexadecimal, octal.

` `console.log( 1_000_000_000.11 ); // 1000000000.11 console.log( 1_000_000_000.1_012 ); // 1000000000.1012 console.log( 0xFF_00_FF ); // 16711935 console.log( 0b1001_0011 ); // 147 console.log( 0o11_17 ); // 591` `

### → Support

• TC39: Stage 3
• Chrome: 75+
• Node: 12.5+

## Bigint data type

Numbers in JavaScript are created using the `Number` constructor function.

The maximum value that can be safely represented using the `Number` data type is (2⁵³ - 1), that is, 9007199254740991. You can see this number using the `Number.MAX_SAFE_INTEGER` construct.

Please note that when a numeric literal is used in JS code, JavaScript processes it, creating an object based on it using the `Number` constructor. The prototype of this object contains methods for working with numbers. This happens with all primitive data types .

What will happen if we try to add something to the number 9007199254740991?

` `console.log( Number.MAX_SAFE_INTEGER ); // 9007199254740991 console.log( Number.MAX_SAFE_INTEGER + 10 ); // 9007199254741000` `

The result of adding `Number.MAX_SAFE_INTEGER` and 10, the second output of `console.log()` , is incorrect. This is due to the fact that JS cannot correctly perform calculations with numbers greater than the value of `Number.MAX_SAFE_INTEGER` . You can deal with this problem by using the `bigint` data `bigint` .

The `bigint` type allows `bigint` to represent integers that are greater than `Number.MAX_SAFE_INTEGER` . Working with BigInt values ​​is similar to working with values ​​of type `Number` . In particular, the language has the function `BigInt()` , with which you can create the corresponding values, and the built-in primitive data type `bigint` , used to represent large integers.

` `var large = BigInt( 9007199254740991 ); console.log( large ); // 9007199254740991n console.log( typeof large ); // bigint` `

JavaScript adds `n` to the end of BigInt literals. For us, this means that such literals can be written by adding `n` to the end of integers.

Now that we have BigInt numbers at our disposal, we can safely perform mathematical operations on large numbers of type `bigint` .

` `var large = 9007199254740991n; console.log( large + 10n ); // 9007199254741001n` `

A number of type `number` is not the same as a number of type `bigint` . In particular, we are talking about the fact that BigInt-numbers can only be integers. As a result, it turns out that arithmetic operations that use the `bigint` and `number` types cannot be performed.

It should be noted that the `BigInt()` function can take various numbers: decimal, binary, hexadecimal, octal. Inside this function, they will be converted to numbers, the decimal number system is used to represent them.

The `bigint` type also supports bit separators:

` `var large = 9_007_199_254_741_001n; console.log( large ); // 9007199254741001n` `

## New Array Methods: .flat () and .flatMap ()

Here we’ll talk about the new prototype methods for the `Array` object — the `.flat()` and `.flatMap()` methods.

### ▍ .flat () method

Now objects of type `Array` have a new method - `.flat(n)` . It returns a new array, allowing recursively to raise the elements of arrays to the specified level `n` . By default, `n` is 1. This method can be passed `n` equal to `Infinity` , which allows you to convert an array with nested arrays into a one-dimensional array.

` `var nums = [1, [2, [3, [4, 5]]]]; console.log( nums.flat() ); // [1, 2, [3, [4,5]]] console.log( nums.flat(2) ); // [1, 2, 3, [4,5]] console.log( nums.flat(Infinity) ); // [1, 2, 3, 4, 5]` `

### ▍ .flatMap () method

When solving everyday tasks, the programmer may sometimes need to process the array using the `.map()` method with its subsequent transformation into a flat structure. For example, create an array containing the numbers and squares of these numbers:

` `var nums = [1, 2, 3]; var squares = nums.map( n => [ n, n*n ] ) console.log( squares ); // [[1,1],[2,4],[3,9]] console.log( squares.flat() ); // [1, 1, 2, 4, 3, 9]` `

The solution to this problem can be simplified by using the `.flatMap()` method. It converts the arrays returned by the callback function passed to it in the same way that it would convert their `.flat()` method with parameter `n` equal to 1.

` `var nums = [1, 2, 3]; var makeSquare = n => [ n, n*n ]; console.log( nums.flatMap( makeSquare ) ); // [1, 1, 2, 4, 3, 9]` `

### ▍ Object.fromEntries () Method

You can extract `:` type pairs from an object `:` can be used using the static `Object` method, which returns an array, each element of which is an array containing, as the first element, a key, and as the second - a value.

` `var obj = { x: 1, y: 2, z: 3 }; var objEntries = Object.entries( obj ); console.log( objEntries ); // [["x", 1],["y", 2],["z", 3]]` `

Now we have at our disposal a static method `Object.fromEntries()` , which allows us to convert a similar structure back into an object.

` `var entries = [["x", 1],["y", 2],["z", 3]]; var obj = Object.fromEntries( entries ); console.log( obj ); // {x: 1, y: 2, z: 3}` `

The `entries()` method was used to facilitate filtering and mapping of data stored in objects. The result is an array. But so far, the task of converting such an array into an object has not had a beautiful solution. It is for solving this problem that you can use the `Object.fromEntries()` method.

` `var obj = { x: 1, y: 2, z: 3 }; // [["x", 1],["y", 2],["z", 3]] var objEntries = Object.entries( obj ); // [["x", 1],["z", 3]] var filtered = objEntries.filter( ( [key, value] ) => value % 2 !== 0 //  ,     ); console.log( Object.fromEntries( filtered ) ); // {x: 1, z: 3}` `

If the `Map` `:` data structure is used to store the pairs `:` , then the data in it is stored in the order they were added to it. At the same time, how the data is stored resembles the array returned by the `Object.entries()` method. The `Object.fromEntries()` method is `Object.fromEntries()` easy to use for transforming `Map` data structures into objects.

` `var m = new Map([["x", 1],["y", 2],["z", 3]]); console.log( m ); // {"x" => 1, "y" => 2, "z" => 3} console.log( Object.fromEntries( m ) ); // {x: 1, y: 2, z: 3}` `

### ▍ Global property globalThis

We are familiar with the `this` used in JavaScript. It does not have some fixed value. Instead, the meaning of `this` depends on the context in which it is accessed. In any environment, the `this` points to a global object when it is accessed from the context of the highest level. This is the global meaning of `this` .

In browser-based JavaScript, for example, the global value for `this` is the `window` object. You can verify this by using the `console.log(this)` construct at the top level of the JavaScript file (in the most external context) or in the browser JS console.

Accessing this in the browser console

The global value of `this` in Node.js points to a `global` object. Inside a web worker, it points to the worker himself. However, getting the global `this` value is not an easy task. The fact of the matter is that you cannot access `this` anywhere. For example, if you try to do this in the constructor of the class, it turns out that `this` points to an instance of the corresponding class.

In some environments, you can use the `self` keyword to access the global value of `this` . This keyword plays the same role as the mechanisms for accessing this value in browsers, in Node.js, and in web workers. Using knowledge of how the global value of `this` is called in different environments, you can create a function that returns this value:

` `const getGlobalThis = () => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global `this`'); }; var globalThis = getGlobalThis();` `

Before us is a primitive polyfill to get the global `this` object. Read more about this here . JavaScript now has the `globalThis` keyword. It provides a universal way of accessing the global value of `this` for different environments and does not depend on the location of the program from which it is accessed.

` `var obj = { fn: function() {  console.log( 'this', this === obj ); // true  console.log( 'globalThis', globalThis === window ); // true } }; obj.fn();` `

## Stable sorting

The ECMAScript standard does not offer a specific array sorting algorithm that JavaScript engines should implement. It only describes the API used for sorting. As a result, using different JS engines, one may encounter differences in the performance of sorting operations and in the stability (stability) of sorting algorithms.

Now the standard requires that sorting arrays be stable. Details on sorting stability can be found here . The essence of this characteristic of sorting algorithms is as follows. The algorithm is stable if the sorting result, which is a modified array, contains elements with the same values ​​that were not affected by the sorting in the same order in which they were placed in the original array. Consider an example:

` `var list = [  { name: 'Anna', age: 21 },  { name: 'Barbra', age: 25 },  { name: 'Zoe', age: 18 },  { name: 'Natasha', age: 25 } ]; //      age [  { name: 'Natasha', age: 25 }  { name: 'Barbra', age: 25 },  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 }, ]` `

Here, the `list` array containing the objects is sorted by the `age` field of these objects. In the `list` array, an object with the `name` property equal to `Barbra` is located before the object with the `name` property equal to `Natasha` . Since the `age` values ​​of these objects are equal, we could expect that in the sorted array these elements will retain the previous arrangement order relative to each other. However, in practice it was impossible to count on this. How exactly the sorted array will be formed depended entirely on the JS engine used.

Now all modern browsers and Node.js use a stable sorting algorithm, called when accessing the `.sort()` array method. This allows you to always, for the same data, get the same result:

` `//    [  { name: 'Barbra', age: 25 },  { name: 'Natasha', age: 25 }  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 }, ]` `

In the past, some JS engines supported stable sorting, but only for small arrays. To increase productivity when processing large arrays, they could take advantage of faster algorithms and sacrifice sort stability.

• Chrome: 70+
• Node: 12+
• Firefox: 62+

## Internationalization API

The internationalization API is designed to organize string comparisons, to format numbers, dates, and times as is customary in various regional standards (locales). Access to this API is organized through the `Intl` object . This object provides constructors for creating sorter objects and objects that format data. The list of locales supported by the `Intl` object can be found here .

### ▍Intl.RelativeTimeFormat ()

In many applications, it is often necessary to output the time in a relative format. It may look like “5 minutes ago,” “yesterday,” “1 minute ago,” and so on. If the website materials are translated into different languages, you have to include all possible combinations of relative constructions describing the time in the site assembly.

JS now has the `Intl.RelativeTimeFormat(locale, config)` constructor , which allows you to create date and time formatting systems for different locales. In particular, we are talking about objects that have a method `.format(value, unit)` , which allows you to generate various relative timestamps. It looks like this:

` `// español ( ) var rtfEspanol= new Intl.RelativeTimeFormat('es', {  numeric: 'auto' }); console.log( rtfEspanol.format( 5, 'day' ) ); // dentro de 5 días console.log( rtfEspanol.format( -5, 'day' ) ); // hace 5 días console.log( rtfEspanol.format( 15, 'minute' ) ); // dentro de 15 minutos` `

### ▍Intl.ListFormat ()

The `Intl.ListFormat` constructor allows `Intl.ListFormat` to combine list items using the words `and` ( ) and `or` ( ). When creating the corresponding object, the constructor is passed the locale and the object with the parameters. Its `type` parameter can be `conjunction` , `disjunction` and `unit` . For example, if we want to combine the elements of `[apples, mangoes, bananas]` using a conjunction object, we get a string of the form `apples, mangoes and bananas` . If you use a disjunction object, we get a string of the form `apples, mangoes or bananas` .

The object created by the `Intl.ListFormat` constructor has a `.format(list)` method that combines lists. Consider an example:

` `// español ( ) var lfEspanol = new Intl.ListFormat('es', {  type: 'disjunction' }); var list = [ 'manzanas', 'mangos', 'plátanos' ]; console.log( lfEspanol.format( list ) ); // manzanas, mangos o plátanos` `

### ▍Intl.Locale ()

The concept of “regional standard” is usually much more than just the name of a language. This may include the type of calendar, information about the time cycles used, and the names of languages. The `Intl.Locale(localeId, config)` constructor `Intl.Locale(localeId, config)` used to create formatted `Intl.Locale(localeId, config)` strings based on the `config` object passed to it.

`Intl.Locale` object created using `Intl.Locale` contains all the specified regional settings. Its `.toString()` method produces a formatted regional standard string.

` `const krLocale = new Intl.Locale( 'ko', {  script: 'Kore', region: 'KR',  hourCycle: 'h12', calendar: 'gregory' } ); console.log( krLocale.baseName ); // ko-Kore-KR console.log( krLocale.toString() ); // ko-Kore-KR-u-ca-gregory-hc-h12` `

## Promises

Currently, JS has the static methods `Promise.all()` and `Promise.race()` . The `Promise.all([...promises])` method returns a promise that is successfully resolved after all the promises passed to the method as an argument are resolved. This promise is rejected in the event that at least one of the promises transferred to it is rejected. The `Promise.race([...promises])` method returns a promise, which is resolved after any of the promises transferred to it is resolved, and is rejected if at least one of such promises is rejected.

The community of JS developers was desperate for a static method, the promise returned that would be resolved after all the promises passed to it would be complete (allowed or rejected). In addition, we needed a method similar to `race()` , which would return a promise waiting for the resolution of any of the promises passed to it.

### ▍ Promise.allSettled () Method

The `Promise.allSettled()` method accepts an array of promises. The promise returned by him is permitted after all the promises are rejected or permitted. The result is that the promise returned by this method does not need a `catch` .

The fact is that this promise is always successfully resolved. The `then` block receives `status` and `value` from each promise in the order they appear.

` `var p1 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val1' ), 2000 ) ); var p2 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val2' ), 2000 ) ); var p3 = () => new Promise(  (resolve, reject) => setTimeout( () => reject( 'err3' ), 2000 ) ); var p = Promise.allSettled( [p1(), p2(), p3()] ).then(  ( values ) => console.log( values ) ); //  [ {status: "fulfilled", value: "val1"}  {status: "fulfilled", value: "val2"}  {status: "rejected", value: "err3"} ]` `

### ▍ Method Promise.any ()

The `Promise.any()` method is similar to `Promise.race()` , but the promise returned by it does not execute the `catch` when one of the promises passed to this method is rejected.

Instead, he awaits the resolution of all promises. If no promises were allowed, then the `catch` block will be executed. If any of the promises is successfully resolved, `then` will be executed.

## Summary

In this article, we looked at some of the JavaScript innovations discussed at the Google I / O 2019 conference. We hope you find something among them that is useful to you.

Dear readers! What do you especially miss in JavaScript?

All Articles