JS Производительность

Производительность JavaScript — совокупность практик оптимизации времени выполнения кода, потребления памяти и отзывчивости UI: измерение узких мест, минимизация блокировки главного потока, эффективная работа с DOM и памятью.

Зачем нужно

Медленный JavaScript прямо влияет на пользовательский опыт: задержки в UI, низкий FPS анимаций, высокое потребление памяти. Оптимизация без измерения бесполезна — сначала профилируй, затем оптимизируй. Знание техник позволяет писать код, дружелюбный к V8 и браузерному рендер-пайплайну.

Где используется

  • Анимации и интерактивные интерфейсы (60fps)
  • Обработка больших массивов данных
  • Рендеринг длинных списков
  • Оптимизация частых обработчиков событий
  • Снижение нагрузки при первой загрузке страницы

Основной контент

Измерение производительности

// Performance API
performance.mark('start');
// ... код для измерения ...
performance.mark('end');
performance.measure('операция', 'start', 'end');
const [measure] = performance.getEntriesByName('операция');
console.log(`Время: ${measure.duration.toFixed(2)}ms`);

// Быстрый замер
console.time('sort');
const arr = Array.from({ length: 100000 }, () => Math.random);
arr.sort((a, b) => a - b);
console.timeEnd('sort');

Оптимизация DOM

// Плохо: множество reflow (перерасчёт layout)
const list = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Пункт ${i}`;
  list.appendChild(li); // reflow на каждой итерации
}

// Хорошо: DocumentFragment — один reflow
const fragment = document.createDocumentFragment;
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Пункт ${i}`;
  fragment.appendChild(li);
}
list.appendChild(fragment);

// Избегать чтение layout-свойств в цикле
// Плохо: forced synchronous layout
elements.forEach(el => {
  const height = el.offsetHeight; // чтение → reflow
  el.style.width = height + 'px'; // запись → invalidate
});

// Хорошо: сначала все чтения, потом все записи
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => { el.style.width = heights[i] + 'px'; });

Throttle и Debounce для событий

// Debounce — выполнить после паузы
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout( => fn(...args), delay);
  };
}

// Throttle — не чаще раза в период
function throttle(fn, limit) {
  let lastCall = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastCall >= limit) {
      lastCall = now;
      fn(...args);
    }
  };
}

window.addEventListener('resize', throttle(handleResize, 100));
input.addEventListener('input', debounce(search, 300));

Мемоизация и кэширование

function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const fib = memoize(function(n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
});

requestAnimationFrame для анимаций

// Плохо: setInterval не синхронизирован с рендером
setInterval(animate, 16);

// Хорошо: rAF выполняется перед каждым кадром
function animate(timestamp) {
  // обновление на основе timestamp
  element.style.transform = `translateX(${timestamp % 200}px)`;
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

Частые ошибки

  • Оптимизация без профилирования — используйте DevTools Performance и Memory Profiler, чтобы найти реальные узкие места, а не предполагаемые.
  • Чтение layout-свойств (offsetWidth, scrollTop) в цикле — вызывает forced synchronous layout (layout thrashing). Вынесите чтение за пределы цикла.
  • Утечки памяти через замыкания и EventListener — слушатели событий, хранящие ссылки на DOM-узлы, мешают сборщику мусора. Удаляйте через removeEventListener.

V8 internals — как JIT влияет на ваш код

JavaScript исполняется двухпластово: Ignition (интерпретатор байт-кода) + TurboFan (оптимизирующий компилятор). Понимание этой пары — основа всех оптимизаций.

Бенчмарки и измерения

Что реально оптимизировать

Утечки памяти и GC

Связанные темы

Ресурсы