При изменении размера окна, прокрутке, проверке содержимого поля ввода и других операциях, если ониОбработчик действийКогда операция сложна или страница часто повторно отображается, в этом случае, если частота запуска событий неограничена, это увеличит нагрузку на браузер, что приведет к очень плохому взаимодействию с пользователем. На этом этапе мы можем использовать методы устранения дребезга (анти-тряски) и дросселя (дросселирования), чтобыУменьшить частоту срабатывания, не влияя на фактический эффект.
например: оптимизация запроса окна поиска, когда ввод поискового запроса должен немедленно инициировать поисковый запрос, защита от сотрясений и регулирование могут объединять несколько запросов в один запрос
портал заметок github, прошел мимо звезды один?
Сначала подготовьте код в html-файле следующим образом:
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
var num = 1;
var content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
Стабилизатор
Дебунс (anti-shake), проще говоря, предотвращает дрожание.
Когда событие запускается постоянно, debounce будетОбъединяйте события без запуска событий,Когда событие не запускается в течение определенного периода времени, событие фактически запускается.
непрямая версия
Версия без немедленного выполнения означает, что функция не будет выполняться сразу после срабатывания события, а будет выполняться через n секунд, при повторном срабатывании события в течение n секунд время выполнения функции будет пересчитано.
const debounce = (func, wait, ...args) => {
let timeout;
return function(){
const context = this;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
},wait);
}
}
Назовите это так:
content.onmousemove = debounce(count,1000);
Немедленная версия
Версия с немедленным выполнением означает, что функция будет выполняться сразу после срабатывания события, а затем эффект функции может продолжать выполняться, если событие не сработает в течение n секунд.
const debounce = (func, wait, ...args) => {
let timeout;
return function(){
const context = this;
if (timeout) cleatTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
},wait)
if(callNow) func.apply(context,args)
}
}
Комбинированная версия
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func,wait,immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
дросселирование
дроссель (дросселирование), когда событие срабатывает непрерывно, гарантированно срабатывает событие в интервале времени.
Когда событие запускается постоянно, дроссель объединяет события в течение определенного периода времени и фактически запускает событие один раз, когда период заканчивается.
Издание с меткой времени
В процессе непрерывного срабатывания события функция будет выполняться немедленно и каждую 1с.
const throttle = (func, wait, ...args) => {
let pre = 0;
return function(){
const context = this;
let now = Date.now();
if (now - pre >= wait){
func.apply(context, args);
pre = Date.now();
}
}
}
версия таймера
В процессе непрерывного запуска события функция не будет выполняться сразу, а будет выполняться каждые 1 с, после остановки запускающего события функция будет выполняться снова.
const throttle = (func, wait, ...args) => {
let timeout;
return function(){
const context = this;
if(!timeout){
timeout = setTimeout(() => {
timeout = null;
func.apply(context,args);
},wait)
}
}
}
Комбинированная версия
На самом деле, разница между версией с отметкой времени и версией с таймером функции регулирования заключается в том, что триггер функции версии с отметкой времени срабатывает в начале периода времени, а триггер функции версии с таймером — по окончании периода времени.
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttle(func, wait ,type) {
if(type===1){
var previous = 0;
}else if(type===2){
var timeout;
}
return function() {
var context = this;
var args = arguments;
if(type===1){
var now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}else if(type===2){
if (!timeout) {
timeout = setTimeout(function() {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
подчеркивание исходного кода
/**
* underscore 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {boolean} immediate 设置为ture时,是否立即调用函数
* @return {function} 返回客户调用函数
*/
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
// 现在和上一次时间戳比较
var last = _.now() - timestamp;
// 如果当前间隔时间少于设定时间且大于0就重新设置定时器
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
// 否则的话就是时间到了执行回调函数
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
// 获得时间戳
timestamp = _.now();
// 如果定时器不存在且立即执行函数
var callNow = immediate && !timeout;
// 如果定时器不存在就创建一个
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
// 如果需要立即执行函数的话 通过 apply 执行
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
- Для реализации кнопки антиклик: как только я запускаю таймер, пока таймер все еще там, независимо от того, как вы нажимаете, функция обратного вызова не будет выполняться. Как только таймер истечет и будет установлен на ноль, его можно щелкнуть снова.
- Для реализации функции отложенного выполнения: при каждом вызове функции антиджиттера будет оцениваться текущий вызов и предыдущий временной интервал, если он меньше необходимого временного интервала, таймер будет пересоздан, а таймер задержка – это установленное время минус предыдущий временной интервал. По истечении времени выполняется соответствующая функция обратного вызова.
/**
* underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。
* 如果想忽略结尾函数的调用,传入{trailing: false}
* 两者不能共存,否则函数不能执行
* @return {function} 返回客户调用函数
*/
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 之前的时间戳
var previous = 0;
// 如果 options 没传则设为空对象
if (!options) options = {};
// 定时器回调函数
var later = function() {
// 如果设置了 leading,就将 previous 设为 0
// 用于下面函数的第一个 if 判断
previous = options.leading === false ? 0 : _.now();
// 置空一是为了防止内存泄漏,二是为了下面的定时器判断
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
// 获得当前时间戳
var now = _.now();
// 首次进入前者肯定为 true
// 如果需要第一次不执行函数
// 就将上次时间戳设为当前的
// 这样在接下来计算 remaining 的值时会大于0
if (!previous && options.leading === false) previous = now;
// 计算剩余时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果当前调用已经大于上次调用时间 + wait
// 或者用户手动调了时间
// 如果设置了 trailing,只会进入这个条件
// 如果没有设置 leading,那么第一次会进入这个条件
// 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
// 其实还是会进入的,因为定时器的延时
// 并不是准确的时间,很可能你设置了2秒
// 但是他需要2.2秒才触发,这时候就会进入这个条件
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 && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
// 没有的话就开启一个定时器
// 并且不能不能同时设置 leading 和 trailing
timeout = setTimeout(later, remaining);
}
return result;
};
};