Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11

Что нового в ECMAScript 2025: с примерами кода и комментариями экспертов

ECMAScript 2025: новинки, примеры и экспертные разборы

7К открытий15К показов
Что нового в ECMAScript 2025: с примерами кода и комментариями экспертов

Очередная версия ECMAScript была одобрена TC39. Публикуем перевод статьи Павла Гжибека о новинках ES2025 с простыми практическими примерами и комментариями экспертов: Анастасии Егоровой, фронтенд-разработчика, автора тг-канала «Код и кофе» и Александра Коротаева, фронтенд-разработчика, автора тг-канала «Трудно быть Коротаевым».

Из новинок этого года:

  • дублирующиеся именованные группы захвата в регулярных выражениях;
  • Новые методы установки для JavaScript
  • Модификаторы шаблонов регулярных выражений
  • Импорт атрибутов
  • Помощники итераторов
  • Promise.try()
  • Float16Array
  • Экранирование регулярных выражений

Дублирующиеся именованные группы захвата в регулярных выражениях теперь разрешены

Группы захвата в регулярных выражениях — мощный инструмент для извлечения данных. Однако до стандарта ECMAScript 2025 использование одинаковых имён именованных групп захвата внутри разных альтернатив (разделённых символом |) приводило к синтаксической ошибке.

С обновлением ES2025 это изменилось: теперь регулярные выражения с дублирующимися именованными группами внутри альтернатив корректны и работают, как ожидается.

Пример:

			const pattern = /ECMAScript(?<version>[0-9]{4})|ES(?<version>[0-9]{2})/;

//

Ранее:

// <ES2025

"ECMAScript2025".match(pattern);

// SyntaxError: Invalid regular expression: /ECMAScript(?<version>[0-9]{4})|ES(?<version>[0-9]{2})/: Duplicate capture group name

Теперь:

// >=ES2025

"ECMAScript2025".match(pattern);

// "2025"
		

Это особенно удобно для случаев, когда одна и та же логика применяется к разным вариантам шаблона — и вы хотите получать результат через одно и то же имя группы.

Мне всегда нравились изящные решения на основе регулярных выражений, особенно когда для решения задачи хватает только этих волшебных строк. Раньше они были совсем сложными для чтения кем-то, кроме того, кто это написал, так как захват значений работал только по группам с числовыми индексами. Затем появилась возможность использовать именованные группы, и всё стало читаемее.

Теперь, с ними закрыли еще одну проблему: разбор строк, где формат данных мог варьироваться (например, даты); строка запроса в GET-параметрах. Короче, везде, где поддержка полного стандарта одной регуляркой превращала её в самостоятельную программу, вывод которой ещё надо было отдельно обработать.
Александр КоротаевФронтенд-разработчик, автор телеграм-канала «Трудно быть Коротаевым»

Новые методы для Set в JavaScript: работа с множествами становится проще

Предложение по расширению возможностей Set делает работу с множествами в JavaScript гораздо мощнее и выразительнее. В стандарте ECMAScript добавлены семь новых методов в прототип Set, которые позволяют выполнять операции теории множеств напрямую:

  • intersection — пересечение
  • union — объединение
  • difference — разность
  • symmetricDifference — симметрическая разность
  • isSubsetOf — проверка на подмножество
  • isSupersetOf — проверка на надмножество
  • isDisjointFrom — проверка на непересекаемость

Пример:

			const setOne = new Set([1, 2, 3]);

const setTwo = new Set([3, 4, 5]);

setOne.intersection(setTwo);
// Set(1) { 3 }

setOne.union(setTwo);
// Set(5) { 1, 2, 3, 4, 5 }

setOne.difference(setTwo);
// Set(2) { 1, 2 }

setOne.symmetricDifference(setTwo);
// Set(4) { 1, 2, 4, 5 }

setOne.isSubsetOf(setTwo);
// false

setOne.isSubsetOf(setTwo);
// false

setOne.isSubsetOf(setTwo);
// false

		

Теперь вместо написания собственных реализаций можно использовать встроенные эффективные интуитивные методы.

Совет: чтобы действительно разобраться, как работают эти методы и где их применять, стоит освежить в памяти базовые принципы теории множеств — это не только полезно для программирования, но и отлично тренирует логику. А MDN объясняет каждый из новых методов.
Когда я впервые начала работать с Set в JavaScript, столкнулась с ощущением, что в них чего-то не хватает. Периодически мне приходилось писать собственные функции, чтобы вычислить пересечения или объединить разные множества. С появлением новых методов об этом можно забыть. Кроме того, большинством перечисленных методов можно пользоваться уже достаточно давно. Например, intersection работает в большинстве современных браузеров. Как такое может быть? Иногда разные фичи появляются в языке раньше, а в спецификацию вносятся намного позже, и именно так и произошло с методами множеств.
Анастасия ЕгороваФронтенд-разработчик, автор телеграм-канала «Код и кофе»

Локальные модификаторы шаблонов в регулярных выражениях: гибкость внутри паттерна

В 2025 году ECMAScript получил поддержку локальных модификаторов регулярных выражений — возможности применять флаги (такие как i для игнорирования регистра или m для многострочного режима) только к части шаблона, а не ко всему выражению.

Это поведение уже знакомо разработчикам, работавшим с Perl, .NET и другими языками. И теперь оно стало частью официального стандарта JavaScript!

Как это работает:

			const pattern = /^(?i:bearer) abc$/;

pattern.test("bearer abc");

// true

pattern.test("Bearer abc");

// true

pattern.test("bEaReR abc");

// true

pattern.test("bearer ABC");

// false
		

В примере выше слово «bearer» написано без учета регистра, за ним следует литерал «abc», который должен быть написан строчными буквами.

Теперь не нужно жертвовать строгостью отдельных частей выражения ради глобального флага. Такой подход делает регулярные выражения гибче и читаемее, особенно при разборе сложных строк с разными требованиями к регистрам и форматированию.

Это особенно полезно при обработке HTTP-заголовков или составных шаблонов, где часть выражения — чувствительная к регистру, а часть — нет.

Добавление локальных модификаторов — действительно очень удобная история. Давайте посмотрим, как можно их использовать в реальной практике.

Валидация пользовательского email, где локальная часть может быть чувствительной к регистру, а домен должен быть написан строчными буквами. На практике такой кейс встречается нечасто, так как обычно почтовые адреса нечувствительны к регистру, однако техническая возможность разделять anastasiia@gmai.com и Anastasiia@gmai.com у поставщиков услуг электронной почты существует.

Благодаря локальным модификаторам ES2025 валидировать такую почту можно с помощью следующего регулярного выражения:
const emailPattern = /^[a-zA-Z0-9._%+-]+@(?-i:[a-z0-9.-]+\.[a-z]{2,})$/i;

Проверим, что текст удовлетворяет шаблону “ASCII текст, эмодзи, ASCII текст”. Для этого будем использовать флаг u, который включает полную поддержку Юникода. Получится такое выражение:
const pattern = /^[A-Za-z0-9]+ (?u:[\p{Emoji}]+) [A-Za-z0-9]+$/;

Проверяем строку “I ❤️ JavaScript”, получаем true, проверяем “JavaScript ❤️” или “I love JavaScript”, получаем false.

Анастасия ЕгороваФронтенд-разработчик, автор телеграм-канала «Код и кофе»

Импорт JSON-модулей с указанием типа: больше контроля и безопасности

В 2025 году ECMAScript стандартизировал механизм импорта ресурсов с уточнением MIME типа — с помощью атрибута type. Он настраивает поведение при нагрузке, организует анализ и оценку импорта. Это стало важным шагом к безопасной и предсказуемой работе с файлами, не являющимися JavaScript-кодом.

С помощью type: “json” вы явно указываете, что импортируемый файл — это JSON, и тем самым активируете встроенный механизм валидации и интерпретации.

Пример:

			// Import

import data from "./data.json" with { type: "json" };

// Dynamic import

const data = await import("./data.json", { with: { type: "json" } });

// Re-export

export { data } from "./data.json" with { type: "json" };
		

Сейчас поддерживаются только JSON-модули, но ожидается расширение на другие типы ресурсов — изображения, CSV, YAML и т.д. Подобный синтаксис уже активно используется в браузерах с поддержкой ES Modules и в некоторых ранних реализациях Node.js.

Не могу не порадоваться, что вся гибкость импортов из бандлеров медленно, но верно перетекает в нативный JS. Попутно сообщая разработчикам бандлеров, как сделать типы импортов очевидными (кто помнит webpack лоадеры, тот улыбнётся).
Александр КоротаевФронтенд-разработчик, автор телеграм-канала «Трудно быть Коротаевым»

Помощники итератора: новая эра в работе с итерациями в JavaScript

Одно из самых ярких нововведений спецификации ES2025 — появление встроенных вспомогательных методов для итераторов. Это делает работу с ними в JavaScript почти такой же выразительной и удобной, как в Rust, избавляя от необходимости в Array.from() и сторонних библиотеках.

Вот список новых встроенных методов:

  • Общие трансформеры: map(), filter(), flatMap()
  • Контроль итерации: take(n), drop(n)
  • Анализ и сбор данных: reduce(), toArray(), forEach()
  • Проверки: some(), every(), find()
  • Создание итератора: Iterator.from() (статический метод)

Методы вроде map, filter, reduce и some повторяют семантику и названия Array.prototype, так что они будут вам знакомы. А некоторые (drop, take) предназначены только для манипуляций с итераторами и отсутствуют в Array.prototype.

Пример:

			const iter = [..."ECMAScript2025"].values();

const iterNumeric = iter.filter((c) => /^\d$/.test(c));

iterNumeric.next();
// { value: '2', done: false }

iterNumeric.next();
// { value: '0', done: false }

iterNumeric.next();
// { value: '2', done: false }

iterNumeric.next();
// { value: '5', done: false }

iterNumeric.next();
// { value: undefined, done: true }

		

Это нововведение даёт лучшее управление памятью: итераторы остаются ленивыми — операции выполняются по мере запроса. Получаем чистый, декларативный код в духе функционального программирования.

До сих пор не нашел применения итераторам во фронтенде, зато в Node.js их можно использовать для парсинга файлов. Теперь еще появился аналог unix утилиты grep — функция filter делает как раз то, что нужно.

Можно взять большой файл с логами, отфильтровать все строки, где есть ошибки и вывести их, забирая по одной. Выходит, что в отличие от массива, методы фильтрации не выполняются для всего объема данных, а вызываются непосредственно перед вызовом iterator.next(). Намного лучше загружать весь файл в память JS, разбирать его на массив и уже потом делать обработку. Ведь когда-то из-за этого падали по памяти пайплайны сборки очень крупных проектов.
Александр КоротаевФронтенд-разработчик, автор телеграм-канала «Трудно быть Коротаевым»

Promise.try(): элегантная обёртка для любой функции

Оборачивать синхронные или асинхронные операции в Promise — частая задача в современном JavaScript. До ES2025 для этого часто использовали сторонние библиотеки вроде p-try. Но теперь в стандарте появился встроенный способ сделать это безопасно и чисто — Promise.try().

Посмотрим на функцию, которая ожидает Promise:

			const handleAction = (action) =>

action

.then((result) => console.log(result))then((result) => console.log(result))

.catch((error) => console.error(error))

.finally(() => console.log("done"));

//Работает идеально, если action — Promise:

handleAction(Promise.resolve("all good"));

// all good

// done

handleAction(Promise.reject("uuuupps"));

// uuuupps

// done

//Но если вы случайно передадите обычную функцию:

handleAction(() => "look ma, no promise!");

// TypeError: action.then is not a function
		

Теперь можно использовать Promise.try(), чтобы всегда вернуть Promise, независимо от типа операции:

			const handleAction = (action) =>

Promise.try(action)

.then((result) => console.log(result))

.catch((error) => console.error(error))

 .finally(() => console.log("done"));

Работает с синхронной функцией:

handleAction(() => "look ma, no promise!");

// look ma, no promise!

// done

И с синхронным исключением:

handleAction(() => {

throw "look me, no promise, but an error!";

});

// look me, no promise, but an error!

// done


		

Получаем поддержку синхронных и асинхронных функций, унифицированный способ написания try/catch-логики и меньше сторонних зависимостей. А ещё упрощаем отложенное выполнение.

Полезно в обработке событий, API-обёртках, принимающих произвольные коллбэки, тестировании и других кейсах.

Promise.try — отличная новинка для унификации и повышения надежности кода. Помимо единообразной обработки как синхронных, так и асинхронных функций, Promise.try может ещё очень удобно обрабатывать ошибки. Чаще всего ошибки в синхронном коде вызывают исключения, а Promise.try перехватывает их и преобразует в отклонения, тем самым предотвращая сбои в приложении.
Анастасия ЕгороваФронтенд-разработчик, автор телеграм-канала «Код и кофе»

Float16Array: новый формат для чисел с половинной точностью

С приходом Float16Array в JavaScript появился ещё один тип массива с числами с плавающей запятой — теперь с 16-битной (половинной) точностью. Ранее были доступны только Float32Array и Float64Array.

			const smallArray = new Float16Array([1.1, 2.2, 3.3]);

console.log(smallArray);

// Float16Array(3) [1.099609375, 2.19921875, 3.30078125]
		

Для разработчиков графических и ML-приложений — это настоящее спасение, особенно в задачах с ограниченной памятью, например, на GPU или в embedded-системах.

Не самое очевидное обновление, конечно, но давайте попробую объяснить, что тут произошло: дело в том, что числа в JS — 32-битные, и огрехи в точности там всё равно есть (попробуйте сложить 0.1 + 0.2 в консоли браузера). Если мы оставим только 16 бит, то точность расчетов выйдет из комнаты.

Но ситуация меняется, когда нам нужно делать операции со значениями выше 2, именно так поступают нейросети при расчёте пути, по которому пойдёт подбор дальнейшей ветки рассуждений. А когда числа для скоринга потребуют больше точности, переходят на 32-битные значения, обычно это происходит когда вариантов выбора уже не так много, как в начале, и экономия оправдана.

А при чем тут JS? Дело в том, что ранее, для работы с API, которые поддерживали 16 бит на число, из JS туда можно было передать только Float32Array, что увеличивало расход памяти вдвое. Это особенно критично для систем, где нужно передавать такие данные очень много и часто.
Александр КоротаевФронтенд-разработчик, автор телеграм-канала «Трудно быть Коротаевым»

RegExp.escape(): экранирование строк для безопасных регулярных выражений

Когда вы создаёте регулярное выражение из внешнего ввода (например, от пользователя), часто возникают ошибки, если строка содержит спецсимволы вроде . или *

До ES2025 приходилось писать собственные экранирующие функции. Теперь же появилось встроенное решение:

			const userInput = "dog.";

const pattern = new RegExp(RegExp.escape(userInput));

"He has two dogs. I have one dog.".replace(pattern, "cat.");

// => "He has two dogs. I have one cat."
		

Это защищает от ошибок и потенциальных уязвимостей и делает поиск по строкам безопаснее.

Как много раз я натыкался на то, что попытки по-быстрому сделать нечеткий поиск на JS, просто засунув строку из input.value в регулярку, ничем хорошим не заканчивались. Теперь появилась утилита, которая решает хотя бы часть проблем, исключая возможность случайно/специально написать регулярное выражение в инпуте, внедрившись в бизнес-логику, как хакер.
Александр КоротаевФронтенд-разработчик, автор телеграм-канала «Трудно быть Коротаевым»
Подведём итоги и поговорим о будущем. Ключевые фичи из ES2025 — модификаторы шаблонов регулярных выражений, методы Set, импорт атрибутов, помощники итераторов, Promise.try(), Float16Array, экранирование регулярных выражений — действительно интересные и полезные обновления. Мой личный топ — модификаторы шаблонов и Promise.try().

Также я с энтузиазмом жду возможностей, которые сейчас находятся на 2-3 этапах включения в спецификацию. Это предложения и идеи, для реализации которых комитет 39 уже нашел решение, и они находятся на стадии черновиков (2 этап); или предложения, уже прошедшие тестирование и готовые к реализации (3 этап). Одна из таких новинок — объект Temporal, который придет на смену объекту Date и устранит значительную часть проблем, с которыми мы сейчас сталкиваемся при манипуляциях с датами.

Кстати, о датах в JavaScript. Если вы периодически задаётесь вопросами, что вернёт new Date(-[]) — нет, не ошибку. Или что будет, если вызвать new Date(“2”), то рекомендую пройти тест https://jsdate.wtf/, удивиться и начать вместе со мной надеяться, что Temporal скоро появится.
Анастасия ЕгороваФронтенд-разработчик, автор телеграм-канала «Код и кофе»
Следите за новыми постами
Следите за новыми постами по любимым темам
7К открытий15К показов