Сегодня мы публикуем вторую часть перевода материала о новшествах JavaScript. Здесь мы поговорим о разделителях разрядов чисел, о BigInt-числах, о работе с массивами и объектами, о globalThis
, о сортировке, об API интернационализации и о промисах.
> Первая часть
1000000000
— это один миллиард в десятичной системе счисления. Но с одного взгляда понять это тяжело. Поэтому, если читатель программы встретит в коде нечто подобное — ему, чтобы правильно это воспринять, придётся внимательно считать нули._
), применение которого позволяет улучшить читабельность длинных чисел. Вот как числа, записанные с использованием разделителя, выглядят в коде:var billion = 1_000_000_000;
console.log( billion ); // 1000000000
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
Number
.Number
, представляет собой (2?? — 1), то есть — 9007199254740991. Увидеть это число можно, воспользовавшись конструкцией Number.MAX_SAFE_INTEGER
.Number
. Прототип данного объекта содержит методы работы с числами. Это происходит со всеми примитивными типами данных.console.log( Number.MAX_SAFE_INTEGER ); // 9007199254740991
console.log( Number.MAX_SAFE_INTEGER + 10 ); // 9007199254741000
Number.MAX_SAFE_INTEGER
и числа 10, выводимый вторым вызовом console.log()
, неверен. Происходит это из-за того, что JS не может правильно выполнять вычисления с числами, превышающими значение Number.MAX_SAFE_INTEGER
. Справиться с этой проблемой можно, воспользовавшись типом данных bigint
.bigint
позволяет представлять целые числа, которые больше, чем Number.MAX_SAFE_INTEGER
. Работа с BigInt-значениями похожа на работу со значениями типа Number
. В частности, в языке имеется функция BigInt()
, с помощью которой можно создавать соответствующие значения, и встроенный примитивный тип данных bigint
, используемый для представления больших целых чисел.var large = BigInt( 9007199254740991 );
console.log( large ); // 9007199254740991n
console.log( typeof large ); // bigint
n
в конец BigInt-литералов. Для нас это означает то, что такие литералы можно записывать, добавляя n
в конец целых чисел.bigint
.var large = 9007199254740991n;
console.log( large + 10n ); // 9007199254741001n
number
— это не то же самое, что число типа bigint
. В частности, речь идёт о том, что BigInt-числа могут быть только целыми. В результате оказывается, что нельзя выполнять арифметические операции, в которых используются типы bigint
и number
.BigInt()
может принимать различные числа: десятичные, двоичные, шестнадцатеричные, восьмеричные. Внутри этой функции они будут преобразованы к числам, для представления которых используется десятичная система счисления.bigint
также поддерживает разделители разрядов:var large = 9_007_199_254_741_001n;
console.log( large ); // 9007199254741001n
Array
— о методах .flat()
и .flatMap()
.Array
имеется новый метод — .flat(n)
. Он возвращает новый массив, позволяя рекурсивно поднимать элементы массивов на указанный уровень n
. По умолчанию n
равно 1. Этому методу можно передать n
, равное Infinity
, что позволяет преобразовывать массив со вложенными массивами в одномерный массив.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]
.map()
с его последующим преобразованием в плоскую структуру. Например, создадим массив, содержащий числа и квадраты этих чисел: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]
.flatMap()
. Он преобразует массивы, возвращаемые переданной ему функцией обратного вызова, так, как преобразовывал бы их метод .flat()
с параметром n
, равным 1.var nums = [1, 2, 3];
var makeSquare = n => [ n, n*n ];
console.log( nums.flatMap( makeSquare ) ); // [1, 1, 2, 4, 3, 9]
ключ:значение
можно, воспользовавшись статическим методом Object
, который возвращает массив, каждый элемент которого является массивом, содержащим, в качестве первого элемента, ключ, а в качестве второго — значение.var obj = { x: 1, y: 2, z: 3 };
var objEntries = Object.entries( obj );
console.log( objEntries ); // [["x", 1],["y", 2],["z", 3]]
Object.fromEntries()
, который позволяет преобразовать подобную структуру обратно в объект.var entries = [["x", 1],["y", 2],["z", 3]];
var obj = Object.fromEntries( entries );
console.log( obj ); // {x: 1, y: 2, z: 3}
entries()
использовался для облегчения фильтрации и маппинга данных, хранящихся в объектах. В результате получался массив. Но до сих пор у задачи преобразования подобного массива в объект не было красивого решения. Именно для решения этой задачи и можно использовать метод Object.fromEntries()
.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}
ключ:значение
используется структура данных Map
, то данные в ней хранятся в порядке их добавления в неё. При этом то, как хранятся данные, напоминает массив, возвращаемый методом Object.entries()
. Метод Object.fromEntries()
легко использовать и для преобразования в объект структур данных Map
.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}
this
, используемым в JavaScript. У него нет некоего жёстко заданного значения. Вместо этого значение this
зависит от контекста, в котором к нему обращаются. В любом окружении ключевое слово this
указывает на глобальный объект в том случае, когда к нему обращаются из контекста самого верхнего уровня. Речь идёт о глобальном значении this
.this
является объект window
. Проверить это можно, воспользовавшись конструкцией console.log(this)
на верхнем уровне JavaScript-файла (в самом внешнем контексте) или в JS-консоли браузера.this
в Node.js указывает на объект global
. Внутри веб-воркера оно указывает на сам воркер. Однако получить глобальное значение this
— задача не из простых. Дело в том, что для этого нельзя где угодно обратиться к this
. Например, если попытаться сделать это в конструкторе класса, то окажется, что this
указывает на экземпляр соответствующего класса.this
можно пользоваться ключевым словом self
. Это ключевое слово играет ту же роль, что и механизмы доступа к подобному значению в браузерах, в Node.js и в веб-воркерах. Используя знания о том, как в разных средах называется глобальное значение this
, можно создать функцию, которая возвращает это значение: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();
this
. Подробнее об этом можно почитать здесь. Теперь в JavaScript имеется ключевое слово globalThis
. Оно даёт универсальный способ обращения к глобальному значению this
для разных сред и не зависит от места программы, из которого к нему обращаются.var obj = { fn: function() {
console.log( 'this', this === obj ); // true
console.log( 'globalThis', globalThis === window ); // true
} };
obj.fn();
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 },
]
list
, содержащий объекты, сортируют по полю age
этих объектов. В массиве list
объект со свойством name
, равным Barbra
, расположен до объекта со свойством name
, равным Natasha
. Так как значения age
этих объектов равны, мы могли бы ожидать, что в отсортированном массиве эти элементы сохранят прежний порядок расположения относительно друг друга. Однако на практике на это рассчитывать было нельзя. То, как именно будет сформирован отсортированный массив, полностью зависело от используемого JS-движка..sort()
. Это позволяет всегда, для одних и тех же данных, получать один и тот же результат:// стабильные результаты сортировки
[
{ name: 'Barbra', age: 25 },
{ name: 'Natasha', age: 25 }
{ name: 'Anna', age: 21 },
{ name: 'Zoe', age: 18 },
]
Intl
. Этот объект предоставляет конструкторы для создания объектов-сортировщиков и объектов, форматирующих данные. Список поддерживаемых объектом Intl
локалей можно найти здесь.Intl.RelativeTimeFormat(locale, config)
, который позволяет создавать системы форматирования даты и времени для различных локалей. В частности, речь идёт об объектах, имеющих метод .format(value, unit)
, который позволяет генерировать различные относительные отметки времени. Выглядит это так:// espanol (испанский язык)
var rtfEspanol= new Intl.RelativeTimeFormat('es', {
numeric: 'auto'
});
console.log( rtfEspanol.format( 5, 'day' ) ); // dentro de 5 dias
console.log( rtfEspanol.format( -5, 'day' ) ); // hace 5 dias
console.log( rtfEspanol.format( 15, 'minute' ) ); // dentro de 15 minutos
Intl.ListFormat
позволяет комбинировать элементы списков с использованием слов and
(и
), и or
(или
). При создании соответствующего объекта конструктору передаётся локаль и объект с параметрами. Его параметр type
может принимать значения conjunction
, disjunction
и unit
. Например, если мы хотим скомбинировать элементы массива [apples, mangoes, bananas]
с помощью conjunction-объекта, то получим строку вида apples, mangoes and bananas
. Если воспользоваться disjunction-объектом — получим строку вида apples, mangoes or bananas
.Intl.ListFormat
, есть метод .format(list)
, который выполняет комбинирование списков. Рассмотрим пример:// espanol (испанский язык)
var lfEspanol = new Intl.ListFormat('es', {
type: 'disjunction'
});
var list = [ 'manzanas', 'mangos', 'platanos' ];
console.log( lfEspanol.format( list ) ); // manzanas, mangos o platanos
Intl.Locale(localeId, config)
используется для создания форматированных строк локалей, основанных на передаваемом ему объекте config
.Intl.Locale
объект содержит в себе все заданные региональные настройки. Его метод .toString()
выдаёт отформатированную строку регионального стандарта.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
Promise.all()
и Promise.race()
. Метод Promise.all([...promises])
возвращает промис, который успешно разрешается после того, как будут разрешены все промисы, переданные методу в виде аргумента. Этот промис оказывается отклонённым в том случаем, если хотя бы один из переданных ему промисов будет отклонён. Метод Promise.race([...promises])
возвращает промис, который разрешается после того, как любой из переданных ему промисов оказывается разрешённым, и отклоняется при отклонении хотя бы одного из таких промисов.race()
, который возвращал бы промис, ожидающий разрешения любого из переданных ему промисов.Promise.allSettled()
принимает массив промисов. Возвращаемый им промис разрешается после того, как все промисы окажутся отклонёнными или разрешёнными. В результате оказывается так, что промис, возвращённый этим методом, не нуждается в блоке catch
.then
получает status
и value
от каждого промиса в порядке их появления.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"}
]
Promise.any()
похож на Promise.race()
, но промис, возвращаемый им, не выполняет блок catch
при отклонении одного из переданных этому методу промисов.catch
. Если же будет успешно разрешён любой из промисов — будет выполнен блок then
.К сожалению, не доступен сервер mySQL