В настоящее время я изучаю debounce в Javascript, и я столкнулся с двумя способами написания функций debounce, которые работают одинаково. Одна из них намного проще, чем обычная функция, а другая, по-видимому, используется всеми.
@index.html
<input type = "text" oninput = "betterFunction()">
@script.js
function getData() {
console.info('Data Fetched')
}
function debounce(callback, delay) {
let timer
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
callback();
}, delay)
}
}
const betterFunction = debounce(getData, 1000)
@index.html
<input type = "text" oninput = "debounce()">
@script.js
let timer
function debounce() {
clearTimeout(timer)
timer = setTimeout(() => {
console.info('Data Fetched');
}, 1000)
}
В чем разница между этими двумя способами устранения дребезга, если они оба дают одинаковый результат? PS: Я удивлен, что я никогда не видел, чтобы кто-то использовал «версию 2», конечно, что-то не так. Может ли кто-нибудь объяснить различия, пожалуйста?
Во второй версии, если вы хотите устранить дребезг нескольких входов, вам нужно определить отдельную функцию debounce()
для каждого из них. В первой версии вы можете повторно использовать одну и ту же функцию.
Это можно использовать, верно, для нескольких входов? <input type = "text" oninput = "debounce(()=>{//code}, 1000)"> функция таймера debounce(cb, delay) { clearTimeout(timer) timer = setTimeout(() => { cb (); }, задерживать) }
Версия 1 лучше, потому что она:
timer
(идентификатор тайм-аута) в пределах времени жизни вызова функцииУ Джоша В. Комо есть информативная статья о борьбе с дребезгом.
Вот его (модифицированная) минимальная версия:
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback(...args);
}, wait);
};
}
Примечание: я заменил callback.apply(null, args)
более лаконичным callback(...args)
const handleMouseMove = debounce((mouseEvent) => {
// Do stuff with the event!
}, 250);
document.addEventListener('mousemove', handleMouseMove); // Add listener
document.removeEventListener('mousemove', handleMouseMove); // Remove listener
В приведенном ниже фрагменте точки рисуются каждый раз, когда пользователь перестает двигать мышь через 250 мс. Каждая точка автоматически удаляется через 2 секунды.
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback(...args);
}, wait);
};
}
const createPoint = (x, y, color) => {
const point = Object.assign(document.createElement('div'), { className: 'point' });
Object.assign(point.style, { top: `${y - 2}px`, left: `${x - 2}px`, background: color });
document.body.append(point);
return point;
};
// Log mouse coordinates when user stops moving mouse after 250ms
const handleMouseMove = debounce(({ clientX: x, clientY: y }) => {
console.info(`Creating MOVE point at: (${x}, ${y})`);
const point = createPoint(x, y, 'white');
// Auto-remove after 1 second
setTimeout(() => {
console.info(`Removing MOVE point at: (${x}, ${y})`);
point.remove();
}, 2000);
}, 250);
// Log mouse coordinates when user stops clicking 250ms
const handleClick = debounce(({ clientX: x, clientY: y }) => {
console.info(`Creating CLICK point at: (${x}, ${y})`);
const point = createPoint(x, y, 'red');
// Auto-remove after 1 second
setTimeout(() => {
console.info(`Removing CLICK point at: (${x}, ${y})`);
point.remove();
}, 2000);
}, 250);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('click', handleClick);
.as-console-wrapper { max-height: 5em !important; }
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { background: #222; position: relative; }
.point { position: absolute; width: 4px; height: 4px; border-radius: 50%; background: #FFF; }
Спасибо за это замечательное объяснение. Я прошел по ссылке, которую вы дали, это было действительно полезно. Однако теперь у меня есть только одно сомнение: внешняя функция, которая инкапсулирует переменную таймера ...., запускается только один раз в начале, после чего вызывается только возвращаемая функция. Я прав?
@VoodooChild, когда вы оборачиваете функцию в debounce
, вы, по сути, привязываете свою собственную локальную timeoutId
к этой функции. Возвращаемая функция — это фактическая функция, которую вы передаете обработчику событий. Всякий раз, когда срабатывает обратный вызов, он имеет ссылку на эту границу timeoutId
на время существования внешней функции. Функция, которая возвращает функцию при вызове, является функцией. debounce
— это, по сути, фабрика, которая возвращает суперфункцию, имеющую собственную ограниченную область видимости. Это все еще функция в конце дня.
Спасибо еще раз, я благодарен за ваши усилия.
Функция отката — это шаблон программирования javascript для задержки выполнения функции, ваша версия 1 является общепринятым шаблоном для реализации функции отката, потому что она лучше, чем версия 2, по причинам, перечисленным MR. Поливихрь.
Шаблон на самом деле использует функцию javascript, называемую closure
, которая позволяет внутренней функции (функции, возвращаемой debounce) получать доступ к своей внешней области видимости (в частности, в данном случае к объявленной переменной timer
).
Вы можете подробно прочитать о замыканиях здесь , чтобы лучше понять, почему версия 1 является предпочтительной версией, но это объяснение/пример замыканий может быть легче понять поначалу.
При изучении debounce я столкнулся с замыканиями и понял, что возвращаемая функция имеет доступ к состоянию своего лексического окружения. Но мой фактический вопрос заключается в том, нужно ли нам вообще замыкание для этого, поскольку мы можем буквально определить «таймер» и вне функции.
Спасибо, что предоставили мне эти ссылки. Пример со встречной дилеммой в значительной степени прояснил большую часть моих недоразумений.
Один параметризован (т. е. вы можете указать задержку и обратный вызов при вызове), а другой — нет. Но, как вы уже признали, результат тот же.