#Что происходит и как распознать неисправность
Обычный debounce возвращает отложенную функцию. Если её реализовать стрелочной обёрткой, она получает лексический this, а не контекст вызова obj.method(); когда таймер срабатывает, this может оказаться undefined или объектом окна.
#Как отличить причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Обёртка — стрелочная функцияArrow function не создаёт собственный this. | Метод объекта обращается к undefined-свойству. | Возвращайте обычную function и передавайте this через apply. |
| Отложенный callback вызывается без контекстаВнутри setTimeout передана голая ссылка на метод. | При прямом вызове obj.method работает, по таймеру — нет. | Используйте стрелочную функцию внутри setTimeout с захваченным контекстом. |
| Метод отвязан при передаче слушателюВ addEventListener вызывается метод без привязки к объекту. | this становится DOM-элементом, а не объектом модели. | Привязывайте объект в точке передачи или используйте обёртку. |
#Пошаговая диагностика и исправление
- Воспроизведите и определите требуемый контекстОткройте проект в редакторе и найдите вызов debounce для метода. Внутри метода временно выведите
console.log(this)в DevTools. Сравните вызовobj.method()и вызов после дебаунса; если контекст различается, проблема локализована. Не печатайте поля, содержащие пароли или токены. - Проверьте форму возвращаемой функцииОткройте реализацию debounce. Если она возвращает
(...args) => { ... }, такая обёртка получает this из места создания, а не из места вызова. Замените её наfunction (...args) { ... }, чтобы сохранить динамический контекст. - Зафиксируйте контекст и аргументы на каждом вызовеВнутри возвращаемой функции присвойте
const context = this, затем в setTimeout вызовите исходную функцию черезfn.apply(context, args).applyзадаёт this и массив аргументов, которые были переданы последнему вызову. Оставьте прежнюю задержку и очистку таймера. - Если метод передаётся как ссылка, привяжите владельцаДля конкретного объекта можно создать
const handler = debounce(obj.search.bind(obj), 250); тогдаthisметода остаётся obj даже при вызове обработчика из DOM. Не делайте глобальное bind(window): оно скрывает ошибку и разрушает объектную модель. - Проверьте изменение аргументов за время задержкиВызовите метод несколько раз подряд с разными строками в течение 250 мс. Ожидается один вызов с последним значением и правильным this. Если вам нужны все вызовы либо первый вызов немедленно, обычный trailing debounce не подходит — выберите другую стратегию.
- Добавьте очистку при уничтожении компонентаЕсли таймер переживает удаление компонента, он может обращаться к уничтоженным узлам. Сохраните идентификатор таймера, чтобы в жизненном цикле компонента вызвать clearTimeout, либо используйте реализацию с cancel(). Проверьте повторное создание компонента и отсутствие поздних ошибок.
- Верните временные логи и проверьте сценарийПовторите действие в браузере, удостоверьтесь, что значения
thisи аргументы корректны, сетевой запрос появляется один раз после задержки. Если поведение стало иным, восстановите первоначальную функцию из резервной копии и уточните требование: leading или trailing вызов.
#Команды и примеры для проверки
Текст
Рабочий пример для консоли браузера; в реальном компоненте предусмотрите отмену отложенного таймера при уничтожении.
function debounce(fn, delay) {
let timer;
return function (...args) {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => fn.apply(context, args), delay);
};
}
const counter = {value: 5, show() {console.log(this.value);}};
counter.delayed = debounce(counter.show, 200);
counter.delayed(); // 5#Как проверить результат и что делать дальше
Одна серия быстрых вызовов приводит к одному запуску метода с нужным this. Неправильную привязку нельзя исправить увеличением времени задержки.