Чотири вбудовані API JavaScript, які замінюють поширені функції Lodash
Як Math.sumPrecise, Map getOrInsert, Array.fromAsync та інші ітераторські допоміжні функції замінюють інструменти Lodash, а також які моменти щодо точності та конкурентності варто знати.
Раніше встановлення Lodash було одним із перших кроків у будь-якому проекті на JavaScript: воно сумувало числа, заповнювало мапи значеннями за замовчуванням та дозволяло послідовно виконувати операції з відкладеною обробкою. У новіших версіях ECMAScript є вбудовані аналоги для цих завдань, тому залежність від Lodash часто є просто додатковим об’ємом файлів. Нижче наведені чотири вбудовані API, те, що вони замінюють, та ситуації, коли звичайне однорядкове пояснення є оманливим.
Підтримка цих нововведень браузерами все ще розвивається, тому перевірте дані про сумісність вашого браузера та версії Node.js перед використанням polyfill. Для більш детального огляду дивіться сім вбудованих функцій JavaScript, які замінюють залежності від утиліт.
Math.sumPrecise() замість _.sum()
Числа в JavaScript є числами з підтримкою формату IEEE 754 double, тому 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(), без необхідності залежностей.