Главная / Статьи

Четыре встроенные API JavaScript, заменяющие распространённые вспомогательные функции Lodash

Как функции Math.sumPrecise, Map.getOrInsert, Array.fromAsync и вспомогательные итераторы заменяют инструменты Lodash, а также какие особенности точности и параллельной обработки следует учитывать.

Издатель: REACTAPP.TOP4 мин чтения888 слов

Источники: The 4 ES2026 Features That Finally Replace Lodash

Раньше установка Lodash была одним из первых шагов в любом проекте на JavaScript: она суммировала числа, заполняла карты значениями по умолчанию и позволяла выполнять последовательные преобразования. В новых версиях ECMAScript уже имеются встроенные аналоги для этих задач, поэтому использование этой зависимости часто приводит лишь к увеличению размера бандла. Ниже приведены четыре встроенные API, что они заменяют, и где обычное однострочное объяснение может вводить в заблуждение.

Поддержка этих новых функций в движках все еще внедряется, поэтому перед использованием полифилов проверьте данные о совместимости вашего браузера и версии Node.js. Для более полного обзора см. семь встроенных функций JavaScript, заменяющих зависимости от утилит.

Math.sumPrecise() вместо _.sum()

Числа в JavaScript представлены в формате IEEE 754 с плавающей точкой, поэтому выражение 0.1 + 0.2 возвращает значение 0.30000000000000004. Функция _.sum() не обеспечивает защиты: это простой цикл с операцией +, поэтому она унаследовывает все ошибки округления. Функция Math.sumPrecise() принимает итерируемый объект и возвращает сумму, как будто она была рассчитана с точностью, округляя результат в конце.

// ❌ The Legacy Lodash/Hack Way
const prices = [0.1, 0.2];
const total = _.sum(prices); // Still prone to precision errors without custom logic


// ✅ The ES2026 Native Standard
const exactTotal = Math.sumPrecise([0.1, 0.2]);
console.log(exactTotal); // 0.3 (Perfect precision)

Одно исправление к приведённому выше комментарию: значения 0.1 и 0.2 уже являются неточными до любых сложений, а их точная сумма округляется до 0.30000000000000004; поэтому метод Math.sumPrecise([0.1, 0.2]) возвращает именно эту ценность, а не 0.3. Этот метод устраняет ошибки, накапливающиеся в результате множества сложений: для массива [1e20, 0.1, -1e20] простое сложение даёт 0, тогда как Math.sumPrecise возвращает 0.1. Поскольку речь идёт не о десятичном типе, для работы с деньгами всё равно следует использовать целые значения в центах или специализированные библиотеки для обработки десятичных чисел. Кроме того, этот метод выбрасывает исключение TypeError при обработке нечисловых значений вместо их принудительной конвертации.

Map.prototype.getOrInsert() вместо has/set/get

Инструменты кэширования и группировки следуют одной и той же схеме: проверяется наличие ключа, при его отсутствии сохраняется значение по умолчанию, затем считывается соответствующее значение. Метод getOrInsert(key, defaultValue) выполняет всё это за один вызов, возвращая существующую запись или вставляя и возвращая значение по умолчанию.

// ❌ The Legacy Way
const userCache = new Map();

function getUser(id) {
  if (!userCache.has(id)) {
    userCache.set(id, { status: 'pending' });
  }
  return userCache.get(id);
}




// ✅ The ES2026 Native Standard
const userCache = new Map();

function getUser(id) {
  // Checks for the key. If missing, inserts the fallback and returns it instantly.
  return userCache.getOrInsert(id, { status: 'pending' });
}

Обратите внимание, что объект запасного значения всё равно создаётся при каждом вызове, даже если он позже игнорируется. Если использование значения по умолчанию требует значительных ресурсов, используйте getOrInsertComputed(key, callback), который запускает функцию-обратный вызов только в случае отсутствия ключа. Оба метода также доступны для WeakMap; информация о реализации в Node.js приведена в статье Node.js 26: Temporal, Map upserts and Undici 8.

Array.fromAsync() для обработки асинхронных итерируемых объектов

Раньше для сбора данных из пагинационного API-клиента или читаемого потока в массив использовалась ручная цикловая конструкция for await...of, которая добавляла элементы в накопитель. Функция Array.fromAsync() обрабатывает асинхронный итерируемый объект, ожидает завершения обработки каждого элемента и возвращает обычный массив.

// The ES2026 Native Standard
async function fetchAllPages(asyncPaginationStream) {
  // Consumes the entire async stream and resolves into a flat array
  const allRecords = await Array.fromAsync(asyncPaginationStream);

  return allRecords;
}

Опциональный второй аргумент позволяет применять функцию к каждому элементу; результат её работы также ожидается, что покрывает сценарий использования асинхронной функции map. Следует помнить, что элементы обрабатываются последовательно. Это подходит для потоков и курсоров, но не является заменой для Promise.all: если у вас есть независимые обещания, которые должны выполняться одновременно, Promise.all по-прежнему остается подходящим инструментом. К тому же буферизация неограниченного потока в памяти противоречит сути потоковой обработки.

Помощники-итераторы вместо _.chain()

Самая весомая причина использования Lodash — это метод _.chain(), который благодаря отложенной оценке позволяет операциям map, filter и take-five над 10 000 элементами прекратиться сразу после получения пяти результатов. Методы массива работают без отложенной оценки: .map() создает полный промежуточный массив, .filter() снова проходит по нему, а затем .slice() отбрасывает большую часть выполненных операций.

Помощники-итераторы, стандартизированные в ES2025, позволяют реализовать такой подход отложенной оценки нативно. Метод .values() возвращает итератор, а методы map, filter и take на нем извлекают по одному элементу за раз, пока метод toArray() не соберет все результаты.

// ❌ The Array Way (Inefficient: Maps and filters all 10,000 items)
const result = massiveArray
  .map(x => heavyComputation(x))
  .filter(x => x > 100)
  .slice(0, 5);



// ✅ The ES2026 Native Lazy Standard
const result = massiveArray
  .values() // Converts to an Iterator
  .map(x => heavyComputation(x))
  .filter(x => x > 100)
  .take(5) // Stops exactly when 5 items are found!
  .toArray();

Каждый элемент проходит через весь пайплайн перед тем, как будет запрошен следующий, поэтому функция heavyComputation выполняется только до тех пор, пока не будут найдены пять совпадений. Недостатки: итераторы предназначены для однократного использования, отсутствуют функции sort и reverse с опозданным выполнением, а для небольших массивов методы с немедленным выполнением работают так же быстро и легче отлаживаются.

Основные выводы

  • Math.sumPrecise() устраняет накопленную ошибку округления, но не может исправить неточные входные данные; для работы с деньгами используйте целые значения в сантах.
  • getOrInsert() заменяет функции has/set/get; для дорогостоящих значений по умолчанию предпочтительнее использовать getOrInsertComputed().
  • Array.fromAsync() последовательно обрабатывает асинхронные итерируемые объекты; для обеспечения параллельности используйте Promise.all.
  • Сочетание метода .values() с вспомогательными функциями итераторов позволяет создавать пайплайны с опозданным выполнением, аналогичные _.chain(), без необходимости в дополнительных зависимостях.