防抖(频繁触发只执行一次)
定义:就是将一段时间内连续的多次触发转化为一次触发。
在无事件触发后的设定时间执行事件,将几次操作合并为一此操作进行。这样一来,只有最后一次操作能被触发。
使用环境:
1,搜索框搜索内容
2, 页面滚动特定距离 显示【返回顶部按钮】
3, 页面大小resize 触发事件
设计思路:
在setTimeout中调用事件处理函数,如果在定时器触发函数执行之前又触发函数,清除定时器。
即只要触发,就会清除上一个计时器,又注册新的一个计时器。直到停止触发 wait 时间后,才会执行回调函数。
不断触发事件,就会不断重复这个过程,达到防止目标函数过于频繁的调用的目的。
以最后一次触发为标准:
/**
* 实现函数的防抖(目的是频繁触发中只执行一次)
* @param {*} func 需要执行的函数
* @param {*} wait 检测防抖的间隔频率
* @param {*} immediate 是否是立即执行 True:第一次,默认False:最后一次
* @return {可被调用执行的函数}
*/
function debounce(func, wati = 500, immediate = false) {
let timer = null
return function anonymous(... params) {
clearTimeout(timer)
timer = setTimeout(_ => {
// 在下一个500ms 执行func之前,将timer = null
//(因为clearInterval只能清除定时器,但timer还有值)
// 为了确保后续每一次执行都和最初结果一样,赋值为null
// 也可以通过 timer 是否 为 null 是否有定时器
timer = null
func.call(this, ...params)
}, wait)
}
}
以第一次触发为标准:
/**
* 实现函数的防抖(目的是频繁触发中只执行一次)
* @param {*} func 需要执行的函数
* @param {*} wait 检测防抖的间隔频率
* @param {*} immediate 是否是立即执行 True:第一次,默认False:最后一次
* @return {可被调用执行的函数}
*/
function debounce(func, wait = 500, immediate = true) {
let timer = null
return function anonymous(... params) {
// 第一点击 没有设置过任何定时器 timer就要为 null
let now = immediate && !timer
clearTimeout(timer)
timer = setTimeout(_ => {
// 在下一个500ms 执行func之前,将timer = null
//(因为clearInterval只能在系统内清除定时器,但timer还有值)
// 为了确保后续每一次执行都和最初结果一样,赋值为null
// 也可以通过 timer 是否 为 null 是否有定时器
timer = null!immediate ? func.call(this, ...params) : null
}, wait)
now ? func.call(this, ...params) : null
}
}
function func() {
console. log('ok')
}
btn. onclick = debounce(func, 500)
防抖总结
/*
func 执行的函数
wait 需要等待的时间
immediate 参数判断是否是立刻执行
*/
function debounce(func, wait, immediate) {
let time
let debounced = function() {
let context = this
if(time) clearTimeout(time)
if(immediate) {
let callNow = !time
if(callNow) func.apply(context, arguments)
time = setTimeout(
()=>{time = null} //见注解
, wait)
} else {
time = setTimeout(
()=>{func.apply(context, arguments)}
, wait)
}
}
debounced.cancel = function() {
clearTimeout(time)
time = null
}
return debounced
}
如何使用解析
// 定义一个函数
function getUserAction(e) {
....
};
var setUseAction = debounce(getUserAction, 10000, true);
节流(频繁触发中缩减频率)
定义:减少一段时间内触发的频率(固定时间做某一件事)
使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
使用环境:
1 连续点击按钮 切 需求为 间隔请求 例如 页面的【刷新】按钮
2 上拉加载获取数据 的【上拉加载】
方法:
时间戳
思路:
初始化时获取时间,每次触发事件时再次获取时间,两次时间间隔等于或大于设定时间,执行事件,初始化时间重置为当前时间,如此循环。
- 如果时间差大于了规定的等待时间,就可以执行一次;目标函数执行以后,就更新 previous 值,确保它是“上一次”的时间。
- 否则就等下一次触发时继续比较。
// 时间戳
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
定时器
设计思路:定时器在延时时间执行过后,重置为null, 定时器为null时,重新设置定时器,如此循环。
- 当定时器不存在,说明可以执行函数,于是定义一个定时器来向任务队列注册目标函数 目标函数执行后设置保存定时器ID变量为空
- 当定时器已经被定义,说明已经在等待过程中。则等待下次触发事件时再进行查看。
// 节流throttle代码(定时器):
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
时间戳和定时器的对比
- 时间戳实现的:先执行目标函数,后等待规定的时间段;
- 计时器实现的:先等待够规定时间,再执行。 即停止触发后,若定时器已经在任务队列里注册了目标函数,它也会执行最后一次。
时间戳+定时器的结合
// 节流throttle代码(时间戳+定时器)
/*
func 执行的函数
wait 等待的时间
options 设置两种类型:
leading:false 表示禁用第一次执行
trailing: false 表示禁用停止触发的回调
*/
function throttle(func, wait, options) {
let time, context, args, result;
let previous = 0;
if (!options) options = {};
let later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
time = null;
func.apply(context, args);
if (!time) context = args = null;
};
let throttled = function() {
let now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (time) {
clearTimeout(time);
time = null;
}
previous = now;
func.apply(context, args);
if (!time) context = args = null;
} else if (!time && options.trailing !== false) {
time = setTimeout(later, remaining);
}
};
return throttled;
}
如何使用
fucntion action (){
....
}
var setAction = throttled(action,1000,{trailing: false})
注意:leading:false 和 trailing: false 不能同时设置。
节流总结代码
【第一次触发:reamining是负数,previous被赋值为当前时间】 【第二次触发:假设时间间隔是500ms,第一次执行完之后,20ms之后,立即触发第二次,则remaining = 500 - ( 新的当前时间 - 上一次触发时间 ) = 500 - 20 = 480 】
/**
* 实现函数的节流 (目的是频繁触发中缩减频率)
* @param {*} func 需要执行的函数
* @param {*} wait 检测节流的间隔频率
* @param {*} immediate 是否是立即执行 True:第一次,默认False:最后一次
* @return {可被调用执行的函数}
*/
function throttle(func, wait) {
let timer = null
let previous = 0 // 记录上一次操作的时间点
return function anonymous(... params) {
let now = new Date() // 当前操作的时间点
remaining = wait - (now - previous) // 剩下的时间
if (remaining <= 0) {
// 两次间隔时间超过频率,把方法执行
clearTimeout(timer); // clearTimeout是从系统中清除定时器,但timer值不会变为null
timer = null; // 后续可以通过判断 timer是否为null,而判断是否有 定时器
// 此时已经执行func 函数,应该将上次触发函数的时间点 = 现在触发的时间点 new Date()
previous = new Date(); // 把上一次操作时间修改为当前时间
func.call(this, ...params);
} else if(!timer){
// 两次间隔的事件没有超过频率,说明还没有达到触发标准,设置定时器等待即可(还差多久等多久)
// 假设事件间隔为500ms,第一次执行完之后,20ms后再次点击执行,则剩余 480ms,就能等待480ms
timer = setTimeout( _ => {
clearTimeout(timer)
timer = null // 确保每次执行完的时候,timer 都清 0,回到初始状态
//过了remaining时间后,才去执行func,所以previous不能等于初始时的 now
previous = new Date(); // 把上一次操作时间修改为当前时间
func.call(this, ...params);
}, remaining)
}
}
}
function func() {
console. log('ok')
}
btn. onclick = throttle(func, 500)