Язык JavaScript развивается в последние годы очень быстрыми темпами. Стандарт ECMAScript выходит ежегодно, и благодаря этому язык постоянно растёт и совершенствуется. Многие разработчики до сих пор продолжают писать в старых паттернах, хотя сам язык уже давно предлагает гораздо более удобные и чистые решения. В этой статье мы разберём наиболее важные и практичные возможности, принятые в период примерно с ES2022 по ES2026, и каждую из них рассмотрим с примером кода.
Top-level await — упрощение ожидания в модуле
Раньше ключевое слово await можно было использовать только внутри async-функции. Это приводило к неудобным конструкциям, когда нужно было загрузить данные на верхнем уровне модуля. Введённый в ES2022 top-level await позволяет ожидать прямо в самом ES-модуле. Это особенно удобно при загрузке динамической конфигурации или установлении соединения с базой данных в момент загрузки модуля.
// Старый способ — обёрнутая async-функция
(async () => {
const config = await fetch('/config.json').then(r => r.json());
init(config);
})();
// Новый способ — top-level await (ES2022)
const config = await fetch('/config.json').then(r => r.json());
init(config);
Эта возможность работает только в ES-модулях, и другие модули, импортирующие данный, дожидаются его загрузки. Она поддерживается в Node.js 14.8 и выше, а также во всех современных браузерах.
Object.groupBy — группировка массива
Группировка данных по какому-либо признаку встречается в практике постоянно. Раньше для этого приходилось вручную писать код через reduce, и такой код часто был сложным для чтения. Введённый в ES2024 метод Object.groupBy решает эту задачу в одну строку. Он принимает функцию, вызываемую для каждого элемента, и группирует элементы по ключу, возвращаемому этой функцией.
const products = [
{ name: 'Яблоко', type: 'фрукт' },
{ name: 'Морковь', type: 'овощ' },
{ name: 'Банан', type: 'фрукт' }
];
// Новый способ (ES2024)
const grouped = Object.groupBy(products, p => p.type);
// { фрукт: [...], овощ: [...] }
Если ключи группировки являются объектами или сложными значениями, используется Map.groupBy, поскольку ключом обычного объекта может быть только строка или символ. Эти методы доступны начиная с Node.js 21 и в последних версиях браузеров.
Promise.withResolvers — упрощение управления промисом
Иногда нужно создать промис, а его функции resolve и reject вызвать в другом месте. Раньше для этого приходилось объявлять внешние переменные и присваивать им значения внутри конструктора промиса. Метод Promise.withResolvers из ES2024 официально стандартизирует этот паттерн и делает код чище.
// Старый способ
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// Новый способ (ES2024)
const { promise, resolve, reject } = Promise.withResolvers();
// Теперь resolve/reject можно вызвать где угодно
button.onclick = () => resolve('нажато');
Это особенно полезно при работе с событийно-ориентированным кодом, очередями и WebSocket-соединениями. Метод поддерживается в Node.js 22 и всех современных браузерах.
structuredClone — глубокое копирование
Получение глубокой копии объекта долгое время было болезненной темой в JavaScript. Многие разработчики использовали приём JSON.parse(JSON.stringify(obj)), однако он терял даты, Map, Set и другие сложные типы. Доступная в браузере и Node.js глобальная функция structuredClone создаёт настоящую глубокую копию с сохранением корректных типов.
const original = {
date: new Date(),
nested: { items: new Set([1, 2, 3]) },
map: new Map([['a', 1]])
};
const copy = structuredClone(original);
// date остаётся Date, Set и Map сохраняются
copy.nested.items.add(4);
// original не изменяется
Следует помнить, что structuredClone не может копировать функции и DOM-узлы — в таких случаях он выбрасывает ошибку. Функция работает начиная с Node.js 17 и во всех современных браузерах.
Иммутабельные методы массивов — toSorted, toReversed, with
Классические методы sort, reverse и splice изменяли исходный массив, что приводило к неожиданным ошибкам в реактивных библиотеках вроде React. Введённые в ES2023 новые методы возвращают новый массив, не трогая исходный. Этой цели служат методы toSorted, toReversed, toSpliced и with.
const numbers = [3, 1, 2];
// Старый способ — исходный массив портится
const sorted = [...numbers].sort();
// Новый способ (ES2023) — исходный массив сохраняется
const sortedNew = numbers.toSorted();
const reversed = numbers.toReversed();
const replaced = numbers.with(0, 99); // [99, 1, 2]
// numbers не изменился: [3, 1, 2]
Кроме того, метод at позволяет получать элемент с конца по отрицательному индексу, а findLast и findLastIndex — искать в массиве начиная с конца. Это делает ваш код заметно точнее и читабельнее.
Temporal — современная работа с датой и временем
Старый объект Date в JavaScript имел множество недостатков, и работа с часовыми поясами и арифметикой дат была крайне трудоёмкой. Новый API Temporal полностью решает эти проблемы. Он предоставляет иммутабельные объекты, точную поддержку часовых поясов и удобные арифметические методы. Temporal сейчас поэтапно появляется в браузерах, и его уже сегодня можно использовать через полифил.
// Сегодняшняя дата (Temporal)
const today = Temporal.Now.plainDateISO();
// Добавить 30 дней — просто и точно
const future = today.add({ days: 30 });
// Разница между двумя датами
const diff = today.until(future);
console.log(diff.days); // 30
В Temporal часовые пояса управляются через явно заданные типы, поэтому такие сложные задачи, как одновременный расчёт времени в двух городах, выполняются без ошибок. Это одно из самых ожидаемых нововведений, входящих в реальную разработку.
Iterator helpers — эффективная обработка потоков
Новые вспомогательные методы для итераторов позволяют работать непосредственно над потоками, не превращая их в массив. Это особенно экономит память при работе с большими или бесконечными последовательностями, поскольку не все элементы загружаются в память одновременно. Методы вроде map, filter, take и drop теперь применяются к итераторам в виде цепочки.
function* naturalNumbers() {
let n = 1;
while (true) yield n++;
}
// Берём только нужную часть из бесконечного потока
const result = naturalNumbers()
.map(n => n * n)
.filter(n => n % 2 === 0)
.take(3)
.toArray();
// [4, 16, 36]
Такой подход поддерживает функциональный стиль программирования и очень удобен при поэтапной обработке больших объёмов данных. Iterator helpers доступны в Node.js 22 и последних браузерах, существенно обогащая возможности языка. Изучив эти нововведения, вы сможете писать более современный и чистый код.