#javascript
#javascript
Вопрос:
Есть кое-что, чего я принципиально не понимаю в том, как работают функции, которые возвращают другие функции. У меня есть следующая функция throttle, которую я использую, чтобы убедиться, что команда не отправляется слишком часто. Это уже полностью удалено из StackOverflow, я этого не писал.
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
function throttle(func, wait, options) {
var context, args, resu<
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function () {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function () {
var now = Date.now();
if (!previous amp;amp; options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout amp;amp; options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return resu<
};
};
Я привязываю ее к ползунку диапазона. Эта суперосновная интерпретация работает отлично
volSlider = document.querySelector("#slider-vol");
volSlider.oninput = throttle(sendVolumeMessage, 100);
function sendVolumeMessage() {
console.log(`Throttled Volume ${volSlider.value}`);
}
Проблема в том, что я хочу также выполнять нерегулируемые действия с этим ползунком. Следующее не работает
volSlider = document.querySelector("#slider-vol");
volSlider.oninput = setVolume();
function setVolume() {
return function () {
console.log(`Volume ${volSlider.value}`);
throttle(sendVolumeMessage, 100);
}
}
function sendVolumeMessage() {
console.log(`Throttled Volume ${volSlider.value}`);
}
В журнале консоли отображается «Volume 30» или любое другое число, но не отображается заданный объем. Чего я не понимаю в том, как работает передача функций внутри функций? Очевидно, что на самом деле я не вызываю функцию throttle внутри моей функции setVolume, но я ни за что на свете не могу понять, почему.
Комментарии:
1.
oninput
похоже, здесь проблема. Он может принимать только один обработчик. Если вы попытаетесь добавить еще одну, предыдущая будет отброшена.2. Однако в коде есть только одно такое назначение.
3. Да, моей первой попыткой было просто использовать два обработчика oninput, и я обнаружил, о чем вы говорите. Именно тогда я начал пытаться создать единый обработчик, который вызывал бы функцию throttle и функцию nonthrottled. Я не рассматривал возможность просто использовать addEventListener вместо oninput, я полагаю, это тоже сработало бы. Хотя я думаю, что мне больше нравится то, что написано CherryDT.
Ответ №1:
Сохраните throttle и вызовите его
volSlider = document.querySelector("#slider-vol");
volSlider.oninput = setVolume();
function setVolume() {
const fnc = throttle(sendVolumeMessage, 100);
return function () {
console.log(`Volume ${volSlider.value}`);
fnc();
}
}
function sendVolumeMessage() {
console.log(`Throttled Volume ${volSlider.value}`);
}
или добавьте два отдельных события с помощью addEventListener
function otherFunction () {
console.log(new Date());
}
volSlider = document.querySelector("#slider-vol");
volSlider.addEventListener('input', throttle(sendVolumeMessage, 100));
volSlider.addEventListener('input', otherFunction);
Комментарии:
1. Спасибо, это именно то, чего мне не хватало. Я не понял, что на самом деле я не вызывал функцию throttle, а просто определял ее.
Ответ №2:
throttle
Функция возвращает новую функцию, но ваш код никогда не использует эту возвращаемую функцию. Имея это в виду, это работает следующим образом:
const throttledSendVolumeMessage = throttle(sendVolumeMessage, 100); // get throttled version of function *once*
volSlider = document.querySelector("#slider-vol");
volSlider.oninput = setVolume();
function setVolume() {
return function () {
console.log(`Volume ${volSlider.value}`);
throttledSendVolumeMessage(); // call the saved throttled version of the function
}
}
function sendVolumeMessage() {
console.log(`Throttled Volume ${volSlider.value}`);
}
(Примечание: важно хранить регулируемую функцию вне области действия внутренней функции, чтобы она вызывалась throttle
только один раз. В противном случае это каждый раз создавало бы новую индивидуально регулируемую версию функции, которая не имела бы никакого эффекта, потому что каждая из этих версий вызывалась бы один раз и выбрасывалась, так что никакого фактического регулирования никогда бы не произошло.)
Однако совершенно бесполезно переносить вашу setVolume
в другую функцию, ее можно упростить до:
const throttledSendVolumeMessage = throttle(sendVolumeMessage, 100); // get throttled version of function *once*
volSlider = document.querySelector("#slider-vol");
volSlider.oninput = setVolume; // no call here anymore
function setVolume() { // doesn't return another function
console.log(`Volume ${volSlider.value}`);
throttledSendVolumeMessage(); // call the saved throttled version of the function
}
function sendVolumeMessage() {
console.log(`Throttled Volume ${volSlider.value}`);
}
Но мы могли бы на самом деле покончить со всеми этими именованными функциями, на которые затем даже ссылаются выше их определения, и сократить код до этого:
const volSlider = document.querySelector("#slider-vol");
const throttledSendVolumeMessage = throttle(function () {
console.log(`Throttled Volume ${volSlider.value}`);
}, 100);
volSlider.oninput = function () {
console.log(`Volume ${volSlider.value}`);
throttledSendVolumeMessage();
};
И, наконец, вы могли бы избавиться от зависимости от volSlider
переменной во внешней области видимости:
const throttledSendVolumeMessage = throttle(function (volSlider) {
console.log(`Throttled Volume ${volSlider.value}`);
}, 100);
document.querySelector("#slider-vol").oninput = function () {
console.log(`Volume ${this.value}`);
throttledSendVolumeMessage(this);
};
Кстати, хотя это и не влияет на вашу текущую проблему: обычно предпочтительнее использовать element.addEventListener('xyz', handler)
over element.onxyz = handler
, потому что последний позволяет назначать только один обработчик (и новое назначение заменит предыдущее), поэтому это может вызвать неожиданные проблемы позже, если вы (или какой-то плохо написанный сторонний код) попытаетесь подключить второго прослушивателя к тому же событию, чтобы сделать что-то еще.
Комментарии:
1. Спасибо за невероятно подробный ответ. Теперь у меня все работает с вашим ответом и ответом эпаскарелло, и теперь я изучаю остальные ваши предложения, чтобы еще больше его очистить.