Цель: создать функцию
debounce(fn, timeout)
, которая принимает функциюfn
и задержкуtimeout
, а возвращает новую функцию, откладывающую вызовfn
до тех пор, пока не пройдетtimeout
миллисекунд бездействия.
Для решения этой задачи вам понадобятся две ключевые функции JavaScript:
setTimeout()
: чтобы запланировать выполнение функции в будущем.clearTimeout()
: чтобы отменить ранее запланированное выполнение, если функция была вызвана снова.Вам нужно будет хранить идентификатор таймера в переменной, доступной через замыкание.
/**
* @param {Function} fn - Исходная функция
* @param {number} timeout - Задержка в мс
*/
function debounce(fn, timeout) {
// Переменная для хранения ID таймера.
// Она находится в замыкании, поэтому сохраняет свое значение между вызовами.
let timerId = null;
// Возвращаем новую функцию-обертку
return function(...args) {
// Сохраняем контекст `this` и аргументы, с которыми была вызвана обертка
const context = this;
// Если таймер уже был запущен, отменяем его.
// Это и есть ключевая логика debounce.
clearTimeout(timerId);
// Запускаем новый таймер.
timerId = setTimeout(() => {
// Когда таймер сработает, вызываем исходную функцию,
// передавая ей сохраненный контекст и аргументы.
fn.apply(context, args);
}, timeout);
};
}
Почему именно так:
timerId
«живет» между вызовами возвращаемой функции, что позволяет управлять одним и тем же таймером.clearTimeout
: Это сердце механизма. Каждый новый вызов сбрасывает предыдущий «план» на выполнение, гарантируя, что только последний вызов в серии инициирует выполнение.apply(context, args)
: Это позволяет сохранить исходный контекст (this
) и передать все аргументы в целевую функцию fn
. Без этого this
был бы потерян, а аргументы бы не дошли.const debounce = (fn, timeout) => {
let timerId = null;
// Возвращаем стрелочную функцию, которая не имеет своего `this`
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => {
// `this` здесь будет унаследован из внешней области, а не из места вызова.
// Для простых функций без контекста это работает отлично.
fn(...args);
}, timeout);
};
};
Почему именно так:
this
(например, при работе с функциями-хелперами, не являющимися методами объектов).fn
является методом объекта (obj.method()
) и использует this
, этот вариант не подойдет, так как this
будет потерян. В таких случаях нужно использовать Решение #1.Напишите функцию debounce
, которая принимает два аргумента:
fn
— функция, выполнение которой нужно отложить.timeout
— время в миллисекундах, которое должно пройти без вызовов, прежде чем fn
будет выполнена.Функция debounce
должна возвращать новую функцию-обертку. При каждом вызове этой обертки внутренний таймер должен сбрасываться. Исходная функция fn
должна быть вызвана только один раз — после того, как с момента последнего вызова пройдет timeout
мс.
// Пример 1: Обработка ввода
const logger = (text) => console.log(`Отправляем запрос: ${text}`);
const debouncedLogger = debounce(logger, 500);
// Пользователь печатает "привет"
debouncedLogger('п');
debouncedLogger('пр');
debouncedLogger('при');
// ...пауза 500 мс...
// В консоли появится: "Отправляем запрос: при" (только последний вызов)
// Пример 2: Клик по кнопке
let clickCount = 0;
const handleClick = () => {
clickCount++;
console.log(`Кнопка нажата ${clickCount} раз`);
};
const debouncedClick = debounce(handleClick, 1000);
// Пользователь быстро кликает 5 раз
// debouncedClick();
// debouncedClick();
// debouncedClick();
// ...
// Через 1 секунду в консоли появится: "Кнопка нажата 1 раз"
debounce
.fn
только один раз.this
и аргументы в исходную функцию fn
.Цель: создать функцию
debounce(fn, timeout)
, которая принимает функциюfn
и задержкуtimeout
, а возвращает новую функцию, откладывающую вызовfn
до тех пор, пока не пройдетtimeout
миллисекунд бездействия.
Для решения этой задачи вам понадобятся две ключевые функции JavaScript:
setTimeout()
: чтобы запланировать выполнение функции в будущем.clearTimeout()
: чтобы отменить ранее запланированное выполнение, если функция была вызвана снова.Вам нужно будет хранить идентификатор таймера в переменной, доступной через замыкание.
/**
* @param {Function} fn - Исходная функция
* @param {number} timeout - Задержка в мс
*/
function debounce(fn, timeout) {
// Переменная для хранения ID таймера.
// Она находится в замыкании, поэтому сохраняет свое значение между вызовами.
let timerId = null;
// Возвращаем новую функцию-обертку
return function(...args) {
// Сохраняем контекст `this` и аргументы, с которыми была вызвана обертка
const context = this;
// Если таймер уже был запущен, отменяем его.
// Это и есть ключевая логика debounce.
clearTimeout(timerId);
// Запускаем новый таймер.
timerId = setTimeout(() => {
// Когда таймер сработает, вызываем исходную функцию,
// передавая ей сохраненный контекст и аргументы.
fn.apply(context, args);
}, timeout);
};
}
Почему именно так:
timerId
«живет» между вызовами возвращаемой функции, что позволяет управлять одним и тем же таймером.clearTimeout
: Это сердце механизма. Каждый новый вызов сбрасывает предыдущий «план» на выполнение, гарантируя, что только последний вызов в серии инициирует выполнение.apply(context, args)
: Это позволяет сохранить исходный контекст (this
) и передать все аргументы в целевую функцию fn
. Без этого this
был бы потерян, а аргументы бы не дошли.const debounce = (fn, timeout) => {
let timerId = null;
// Возвращаем стрелочную функцию, которая не имеет своего `this`
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => {
// `this` здесь будет унаследован из внешней области, а не из места вызова.
// Для простых функций без контекста это работает отлично.
fn(...args);
}, timeout);
};
};
Почему именно так:
this
(например, при работе с функциями-хелперами, не являющимися методами объектов).fn
является методом объекта (obj.method()
) и использует this
, этот вариант не подойдет, так как this
будет потерян. В таких случаях нужно использовать Решение #1.Напишите функцию debounce
, которая принимает два аргумента:
fn
— функция, выполнение которой нужно отложить.timeout
— время в миллисекундах, которое должно пройти без вызовов, прежде чем fn
будет выполнена.Функция debounce
должна возвращать новую функцию-обертку. При каждом вызове этой обертки внутренний таймер должен сбрасываться. Исходная функция fn
должна быть вызвана только один раз — после того, как с момента последнего вызова пройдет timeout
мс.
// Пример 1: Обработка ввода
const logger = (text) => console.log(`Отправляем запрос: ${text}`);
const debouncedLogger = debounce(logger, 500);
// Пользователь печатает "привет"
debouncedLogger('п');
debouncedLogger('пр');
debouncedLogger('при');
// ...пауза 500 мс...
// В консоли появится: "Отправляем запрос: при" (только последний вызов)
// Пример 2: Клик по кнопке
let clickCount = 0;
const handleClick = () => {
clickCount++;
console.log(`Кнопка нажата ${clickCount} раз`);
};
const debouncedClick = debounce(handleClick, 1000);
// Пользователь быстро кликает 5 раз
// debouncedClick();
// debouncedClick();
// debouncedClick();
// ...
// Через 1 секунду в консоли появится: "Кнопка нажата 1 раз"
debounce
.fn
только один раз.this
и аргументы в исходную функцию fn
.Редактор кода намеренно скрыт на мобильном.
Поверь, так лучше: я оберегаю тебя от искушения писать код в неидеальных условиях. Маленький экран и виртуальная клавиатура — не лучшие помощники для программиста.
📖 Сейчас: Изучи задачу, продумай решение. Действуй как стратег.
💻 Потом: Сядь за компьютер, открой сайт и реализуй все идеи с комфортом. Действуй как код-джедай!