ECMAScript 2024: что будет добавлено в стандарт

Аватарка пользователя Рафаил Агазода

Рассмотрели новые методы в ECMAScript 2024, которые планируют внедрить, и рассказали, зачем они нужны и как их будут использовать.

Обложка поста ECMAScript 2024: что будет добавлено в стандарт

Рассмотрели новые методы, которые запланированы к внедрению в ECMAScript 2024 и рассказали, зачем они нужны и как их будут использовать.

  1. Что такое ECMAScript и при чем тут JavaScript
  2. Замена символов на Unicode
  3. Асинхронное атомарное ожидание для ECMAScript
  4. Синтаксис и семантика для операций над множествами
  5. Изменяемые и расширяемые ArrayBuffer
  6. Группировка массивов
  7. Promise.withResolvers
Используете ли вы обновления стандартов в работе?
Да, обновляю код, если появляются новые методы
Нет, привык писать код с использованием проверенных способов

Что такое ECMAScript и при чем тут JavaScript

ECMAScript (ES) – это стандарт для скриптовых языков, включая JavaScript. На самом деле, JavaScript является наиболее широко используемой реализацией стандарта ECMAScript.

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

Как стандарт, ECMAScript оказывает глубокое влияние на развитие языка JavaScript. Изменения и дополнения, внесенные в стандарт ECMAScript, определяют новые функции и возможности, которые становятся доступными в реализации JavaScript.

Подробнее о ECMAScript, версиях и отличиях от JavaScript можно прочитать здесь.

Замена символов на Unicode

Значения строк в ECMAScript представляют собой последовательность из 16-битных беззнаковых целочисленных значений. Однако ECMAScript не накладывает никаких ограничений или требований к ним.

В правильно сформированных строках каждое целочисленное значение в последовательности представляет собой отдельную единицу UTF-16 юникодного текста. Однако не все последовательности представляют собой UTF-16 юникодный текст.

Также в правильно сформированных строках кодовые единицы в диапазоне от 0xD800 до 0xDBFF (ведущие суррогаты) и от 0xDC00 до 0xDFFF (заключительные суррогаты) должны появляться парами и по порядку. Строки с непарными или неправильно упорядоченными суррогатами считаются неправильно сформированными.

Новый стандарт предлагает ввести метода для проверки, является ли строка правильно сформированной. Метод должен улучшить производительность и увеличит понятность для читателей кода.

Также предлагается добавить метод для замены любых одиночных или неправильно упорядоченных суррогатов, если они присутствуют, на U+FFFD (символ замены в Юникоде). Эта операция снижает вероятность того, что код будет несовместимым.

Методы String.prototype.isWellFormed и String.prototype.toWellFormed, могут быть использованы, например, так:

			if (!someString.isWellFormed()) {
  someString = someString.toWellFormed(); 
  }.
		

Алгоритм проверки проверяет символы UTF-16, проходя по строке, и образует пары UTF-16 суррогатов.

В JavaScript это можно сделать с помощью регулярного выражения:

			!/\p{Surrogate}/u.test(str);
		

Или более явно, используя алгоритм вроде:

			function isWellFormed(str) {
  for (let i = 0; i < str.length; ++i) {
    const isSurrogate = (str.charCodeAt(i) & 0xF800) == 0xD800;
    if (!isSurrogate) {
      continue;
    }
    const isLeadingSurrogate = str.charCodeAt(i) < 0xDC00;
    if (!isLeadingSurrogate) {
      return false; // непарный завершающий суррогат
    }
    const isFollowedByTrailingSurrogate = i + 1 < str.length && (str.charCodeAt(i + 1) & 0xFC00) == 0xDC00;
    if (!isFollowedByTrailingSurrogate) {
      return false; // непарный ведущий суррогат
    }
    ++i;
  }
  return true;
}
		

Асинхронное атомарное ожидание для ECMAScript

Предлагается метод об “асинхронном атомарном ожидании” для ECMAScript, преимущественно для использования в агентах, которым запрещено блокирование.

Atomics.wait в JavaScript – это метод, который предоставляет механизм ожидания изменения значения TypedArray внутри SharedArrayBuffer. Он блокирует исполнение текущего потока до тех пор, пока значение в указанной позиции не станет другим.

Предполагается, что новый метод Atomic.waitSync поможет не блокировать отдельные процессы, которые необходимо выполнять параллельно Atomic.wait, но подробной документации попросту нет.

Синтаксис и семантика для операций над множествами

В регулярных выражениях ECMAScript предлагается добавить синтаксис и семантику для операций над символьными множествами:

  1. разность/вычитание (в A, но не в B);
  2. пересечение (в обоих A и B);
  3. вложенные символьные классы (необходимы для выполнения вышеперечисленного).

Также предлагается добавить свойства Unicode для строк и литералы строк в символьные классы.

Шаблоны регулярных выражений ECMAScript уже поддерживают одну операцию над множествами в ограниченной форме: можно создать объединение символов типа \s или \p{Decimal_Number}.

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

  1. разность/вычитание как [A--B];
  2. пересечение как [A&&B];
  3. вложенный символьный класс как [A--[0-9]].

В этих примерах A и B могут рассматриваться как заполнители для символьного класса (например, [a-z]) или спецсимвола свойства (например, \p{ASCII}).

Изменяемые и расширяемые ArrayBuffer

ArrayBuffer позволяет обрабатывать двоичные данные в памяти. В 2024 году предлагается расширить конструкторы ArrayBuffer и чтобы добавить максимальное значение буферов.

Аналогично, SharedArrayBuffer тоже позволять расширять максимальное значение размера памяти.

Это сделано для того, чтобы не выделять новые буферы и не копировать данные.

Кроме того, создание новых буферов и перенос данных лишний раз фрагментирует адресное пространство в системах с 32-битной архитектурой.

Расширяемые ArrayBuffer и автоматическое отслеживание TypedArrays помогут решить эту проблему.

Группировка массивов

Предлагается упростить группировку элементов в массиве. Это крайне распространенная операция, которую выполняют через GROUP BY в SQL или MapReduce.

Решат проблему два метода: Object.groupBy и Map.groupBy.

Первый возвращает объект с прототипом null, что позволяет использовать удобную деструктуризацию и предотвращает случайные коллизии с глобальными свойствами объекта.

Второй возвращает обычный экземпляр Map, что позволяет группировать данные по сложным типам ключей.

			const array = [1, 2, 3, 4, 5];

// `Object.groupBy` группирует элементы по произвольному ключу.
// В данном случае мы группируем по четным/нечетным ключам
Object.groupBy(array, (num, index) => {
  return num % 2 === 0 ? 'even': 'odd';
});
// =>  { odd: [1, 3, 5], even: [2, 4] }

// `Map.groupBy` возвращает элементы в виде Map и полезен для группировки
// с использованием ключа объекта.
const odd  = { odd: true };
const even = { even: true };
Map.groupBy(array, (num, index) => {
  return num % 2 === 0 ? even: odd;
});
// =>  Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }
		

Promise.withResolvers

При создании Promise пользователь должен передать функцию executor callback, которая принимает два аргумента:

  1. resolve – функцию, запускающую разрешение Promise,
  2. reject – функцию, отклоняющую Promise.

Это хорошо работает, если callback может встроить вызов асинхронной функции, например, в обработчике событий.

			const promise = new Promise((resolve, reject) => {
  asyncRequest(config, response => {
    const buffer = [];
    response.on('data', data => buffer.push(data));
    response.on('end', () => resolve(buffer));
    response.on('error', reason => reject(reason));
  });
});
		

Однако часто разработчики хотят настроить поведение разрешения и отклонения обещания. Сейчас для реализации приходится создавать громоздкие конструкции вроде:

			let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});
asyncRequest(config, response => {
  const buffer = [];
  response.on('callback-request', id => {
    promise.then(data => callback(id, data));
  });
  response.on('data', data => buffer.push(data));
  response.on('end', () => resolve(buffer));
  response.on('error', reason => reject(reason));
});
		

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

В ECMAScript 2024 предлагается добавить статический метод withResolvers в Promise, который вернет обещание вместе с доступными функциями разрешения и отклонения.

			const { promise, resolve, reject } = Promise.withResolvers();
		

Заключение

Ознакомиться с оригинальной документацией по правкам в ECMAScript можно на GitHub TC39. О том, что было добавлено в 2023 году, рассказывали в этой статье.

Пишите в комментариях, насколько важны те исправления, которые предложили разработчики, и каких правок ECMAScript все еще не хватает.

JavaScript
Языки программирования
TypeScript
7357