Что убивает перформанс JS

Сводный чек-лист по разборам AsForJS: типичные «убийцы» производительности, которые встречаются в проде чаще всего.

TOP-killers

1. Аллокация в горячем цикле

// УБИВАЕТ: миллион промежуточных объектов
for (let i = 0; i < N; i++) {
  const tmp = { x: arr[i], y: i };   // мусор для GC
  process(tmp);
}

// Лучше: переиспользуй
const tmp = { x: 0, y: 0 };
for (let i = 0; i < N; i++) {
  tmp.x = arr[i];
  tmp.y = i;
  process(tmp);
}

2. Универсальная функция на разных формах

Monomorphic Polymorphic Megamorphic

3. Forced reflow / layout thrashing

// УБИВАЕТ: чтение layout-свойства в цикле = synchronous reflow
elements.forEach(el => {
  const h = el.offsetHeight;       // reflow
  el.style.width = h + 'px';        // invalidate
});

// Лучше: read all → write all
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => { el.style.width = heights[i] + 'px'; });

4. Слушатели событий без removeEventListener

Любая ссылка на DOM-узел из callback мешает GC. На SPA это утечка памяти за минуты.

5. Большие JSON.stringify синхронно

Блокирует main thread. На больших данных — Worker, потоковая сериализация или incremental.

6. Регэкспы без g/y в цикле

/abc/.test(...) создаёт matchstate каждый раз. Кэшируй regex как const.

7. Promise chain с лишними await

// УБИВАЕТ: 3 микротаски
const a = await fetch(...);
const b = await fetch(...);
const c = await fetch(...);

// Лучше: параллельно
const [a, b, c] = await Promise.all([fetch(...), fetch(...), fetch(...)]);

8. Скрытый O(n²) через arr.includes в цикле

Сложность алгоритмов в JS

Источники

См. также