Как использовать функцию, которая возвращает другую функцию?

#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. Спасибо за невероятно подробный ответ. Теперь у меня все работает с вашим ответом и ответом эпаскарелло, и теперь я изучаю остальные ваши предложения, чтобы еще больше его очистить.