函数防抖(debounce)和函数节流(throttle)是两种常用优化高频率触发事件的方法。
一个经典的比喻:
每天上下班大厦的电梯。把电梯完成一次运送,类比为一次函数的执行和响应。
假设电梯超时设定为15秒,不考虑电梯容量限制。
电梯第一个人进来后,15秒后电梯准时关门运送,这是节流。
电梯第一个人进来后,等待15秒。如果等待过程中又有人进来,等待时间会重新计时,直到15秒后电梯关门开始运送,这是防抖。
1. 函数防抖
1.1 含义
函数防抖:当事件被触发后,函数不会立即执行,而是会等待指定的延迟时间。如果在这个延迟时间内事件再次被触发,那么函数的执行会取消,并重新计算延迟时间。只有当最后一次触发事件经过完整的延迟时间后,函数才会执行。
1.2 使用场景
1. 按钮点击
2. 调整窗口大小
3. 搜索框输入
4. 滚动事件等
1.3 具体实现
/**
* 函数防抖
* @param {*} callback 回调函数
* @param {*} duration 经过时间
*/
function debounce(callback, duration) {
let timer = null;
return () => {
// 清空之前已存在的计时器
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() => {
callback.apply(this, arguments);
}, duration);
};
}
举例
let handle = debounce((width) => {
console.log(width);
}, 1000)
window.onresize = () => {
handle(document.documentElement.clientWidth)
};
2. 函数节流
2.1 含义
函数节流:预先设定一个执行频率,在事件被触发时,如果距离上次执行的时间间隔大于等于设定的频率,则执行函数,并重置计时器。如果小于设定的频率,则忽略此次触发。确保函数在一段时间内最多只执行一次,有效避免高频事件导致的性能问题。
2.2 使用场景
1. 限制输入框输入事件
2. 拖拽事件
3. 高频点击提交等
2.3 具体实现
方法一:计时器,立即触发但需要等待指定时间
/**
* 函数节流
* @param {*} callback
* @param {*} duration
* @returns
*/
function throttle(callback, duration) {
let timer = null;
return function () {
if (timer) {
return;
}
timer = setTimeout(() => {
callback.apply(this, arguments);
timer = null;
}, duration);
};
}
方法二:时间戳,立刻执行,立刻停止
function throttle(callback, duration) {
let time;
return function () {
// 如果之前没有调用 || 距上次执行时间超过指定时间
if (!time || Date.now() - time >= duration) {
callback.apply(this, arguments);
time = Date.now();
}
};
}
方法三:二者混合,完整的函数节流
function throttle(callback, duration, immediately) {
if (immediately === undefined) {
immediately = true;
}
if (immediately) {
let time;
return function () {
if (!time || Date.now() - time >= duration) {
callback.apply(this, arguments);
time = Date.now();
}
};
} else {
let timer = null;
return function () {
if (timer) {
return;
}
timer = setTimeout(function () {
callback.apply(this, arguments);
timer = null;
}, duration);
};
}
}
举例
function mousemove(e) {
console.log(e);
}
let mousemoveFn = throttle(mousemove, 1000);
document.onmousemove = function (e) {
mousemoveFn(e);
}