JS Tower
JavaScript & TypeScriptМодуль 6: ООП и паттерны

Полезные паттерны

Debounce, throttle, memoization и другие

Цель урока

В этом уроке ты научишься:

  • Оптимизировать частые вызовы функций
  • Кэшировать результаты вычислений
  • Использовать каррирование и композицию

Debounce

Откладывает выполнение до прекращения вызовов:

function debounce(fn, delay) {
  let timeoutId;
  
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

// Использование
const search = debounce((query) => {
  console.log("Поиск:", query);
}, 300);

// При быстром вводе вызовется только последний
search("a");
search("ab");
search("abc"); // Только этот выполнится

Применение: поиск при вводе, изменение размера окна.


Throttle

Ограничивает частоту вызовов:

function throttle(fn, delay) {
  let lastTime = 0;
  
  return function(...args) {
    const now = Date.now();
    if (now - lastTime >= delay) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

// Использование
const handleScroll = throttle(() => {
  console.log("Скролл");
}, 100);

// Вызовется максимум раз в 100мс
window.addEventListener("scroll", handleScroll);

Применение: скролл, перемещение мыши.


Memoization

Кэширование результатов:

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

// Использование
const factorial = memoize(function(n) {
  console.log("Вычисляем", n);
  if (n <= 1) return 1;
  return n * factorial(n - 1);
});

console.log(factorial(5)); // Вычисляет
console.log(factorial(5)); // Из кэша
console.log(factorial(6)); // Частично из кэша

Currying

Преобразование функции с несколькими аргументами:

// Обычная функция
function add(a, b, c) {
  return a + b + c;
}

// Каррированная
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

console.log(curriedAdd(1)(2)(3)); // 6

// Универсальная функция каррирования
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...args2) {
      return curried.apply(this, args.concat(args2));
    };
  };
}

const curriedAdd2 = curry(add);
console.log(curriedAdd2(1)(2)(3)); // 6
console.log(curriedAdd2(1, 2)(3)); // 6
console.log(curriedAdd2(1)(2, 3)); // 6

Применение: создание специализированных функций.

const multiply = curry((a, b) => a * b);
const double = multiply(2);
const triple = multiply(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

Pipe и Compose

// Pipe — выполнение слева направо
const pipe = (...fns) => (x) => 
  fns.reduce((acc, fn) => fn(acc), x);

// Compose — выполнение справа налево
const compose = (...fns) => (x) => 
  fns.reduceRight((acc, fn) => fn(acc), x);

// Использование
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;

const transform = pipe(addOne, double, square);
console.log(transform(2)); // ((2 + 1) * 2)² = 36

const transform2 = compose(square, double, addOne);
console.log(transform2(2)); // то же самое

Once

Функция, выполняющаяся один раз:

function once(fn) {
  let called = false;
  let result;
  
  return function(...args) {
    if (!called) {
      called = true;
      result = fn.apply(this, args);
    }
    return result;
  };
}

const initialize = once(() => {
  console.log("Инициализация");
  return { initialized: true };
});

initialize(); // "Инициализация"
initialize(); // ничего
initialize(); // ничего

Практика

Задание 1: Debounce

Задача: Реализуй debounce.

Loading...
Ваш вывод:

Задание 2: Memoize

Задача: Реализуй мемоизацию.

Loading...
Ваш вывод:

Задание 3: Curry

Задача: Создай каррированную функцию.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
10
15

Проверь себя

  1. Чем debounce отличается от throttle?
  2. Когда использовать мемоизацию?
  3. Что такое каррирование?