В процессе фронтенд-разработки нам часто требуется привязать какие-то постоянно срабатывающие события, такие как изменение размера, прокрутка, перемещение мыши и т. д., но иногда мы не хотим выполнять функции так часто во время непрерывного срабатывания событий.
Обычно в этом случае, как мы это решаем? В общем, стабилизация изображения и дроссель — лучшее решение.
Давайте посмотрим на ход событий, продолжающих выполнять часто срабатываемые функции, - какая ситуация.
Код в html файле выглядит следующим образом
<div id="content"
style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
let num = 1;
const content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
В приведенном выше коде элемент div привязан к событию mousemove, которое будет продолжать срабатывать, когда мышь перемещается в область div (серого), что приводит к частому выполнению функции. Эффект следующий
Видно, что без других операций функция выполняется часто, что приводит к очень быстрому изменению данных на странице. Итак, давайте посмотрим, как анти-шейк и троттлинг решают эту проблему.
Отказаться
Так называемый антишейк означает, что функция выполняется через n секунд после срабатывания события, и если событие сработает снова в течение n секунд, время выполнения функции будет пересчитано.
Функция защиты от сотрясений делится на версию с немедленным выполнением и версию с немедленным выполнением.
Версия без немедленного исполнения:
function debounce(func, wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
Версия без немедленного выполнения означает, что функция не будет выполняться сразу после срабатывания события, а будет выполнена через n секунд, при повторном срабатывании события в течение n секунд время выполнения функции будет пересчитано.
Мы по-прежнему используем приведенный выше пример привязки события mousemove.Через приведенную выше функцию защиты от сотрясений мы можем использовать ее следующим образом.
content.onmousemove = debounce(count,1000);
Эффект следующий
Как видите, функция не выполняется до 1 секунды после запуска события, и если я запускаю событие в течение 1 секунды после запуска события, время выполнения функции пересчитывается.
В коде вышеприведенной функции защиты от сотрясений также необходимо обратить внимание на передачу this и параметров
const context = this;
const args = [...arguments];
Код функции debounce использует эти две строки кода для получения параметров this и , так что функция this, возвращаемая функцией debounce, будет указывать на то же самое и по-прежнему принимать параметр e.
Немедленно выполнить издание:
function debounce(func,wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
Версия с немедленным выполнением означает, что функция будет выполняться сразу после срабатывания события, а затем эффект функции может продолжать выполняться, если событие не сработает в течение n секунд.
Используйте тот же метод, что и выше, эффект следующий
В процессе разработки нам нужно решить, какую версию функции защиты от сотрясений нам нужно использовать в соответствии с различными сценариями.Вообще говоря, вышеуказанные функции защиты от сотрясений могут удовлетворить потребности большинства сценариев. Но мы также можем комбинировать версию с немедленным выполнением и версию с немедленным выполнением функции защиты от сотрясений, чтобы реализовать окончательную версию функции защиты от сотрясений с двумя мечами.
Версия с двойными мечами:
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
if (immediate) {
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
}
дроссель
Так называемое регулирование относится к непрерывному запуску события, но выполнению функции только один раз в n секунд.Регулирование снижает частоту выполнения функции.
Для регулирования обычно есть два способа достижения, а именно версия с меткой времени и версия с таймером.
Версия временной метки:
function throttle(func, wait) {
var previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
Используйте следующим образом
content.onmousemove = throttle(count,1000);
Эффект следующий
Видно, что в процессе непрерывного срабатывания события функция будет выполняться сразу и каждую 1с.
Версия таймера:
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
Используйте так же, как указано выше, эффект следующий
Видно, что в процессе непрерывного срабатывания события функция будет выполняться не сразу, а будет выполняться каждые 1 с.После остановки события функция будет выполняться снова.
Мы должны быть в состоянии легко обнаружить, что разница между версией функции регулирования с отметкой времени и версией с таймером заключается в том, что версия функции с отметкой времени запускается в начале периода времени, а версия функции с таймером срабатывает во время период времени, когда он заканчивается.
Точно так же мы можем также объединить функцию регулирования версии с отметкой времени и версию с таймером, чтобы реализовать функцию регулирования версии с двойным мечом.
Версия с двойными мечами:
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttle(func, wait ,type) {
if(type===1){
let previous = 0;
}else if(type===2){
let timeout;
}
return function() {
let context = this;
let args = arguments;
if(type===1){
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}else if(type===2){
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
Справочная статья: