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 );
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 );
→ 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 );
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 );
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 );
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 );
→ Support
- TC39: Stage 3
- Chrome: 67+
- Node: 10.4+
- Firefox: 68+
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() );
→ Support
▍ .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 );
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 ) );
→ Support
▍ 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 );
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 );
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 };
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 );
→ Support
▍ 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 );
→ Support
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 } ];
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:
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.
→ Support
- 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:
→ Support
▍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:
→ Support
▍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 );
Here you can read about identifiers and locale tags in Unicode.
→ Support
- TC39: Stage 3
- Chrome: 74+
- Node: 12+
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 ) );
→ Support
▍ 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?