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)); // 15Pipe и 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
Проверь себя
- Чем debounce отличается от throttle?
- Когда использовать мемоизацию?
- Что такое каррирование?