概念
防抖就是在事件触发后等待n秒再执行对事件的逻辑处理函数。
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce
(防抖)的方式来减少调用频率,同时又不影响实际效果。
主要用到了setTimeout
去延迟函数执行时间。但当事件触发了很多次,对应也有很多处理函数在等待执行。而我们只需要事件在停止触发后一段时间执行一次函数。所以,在事件触发后,需要取消上次执行函数的定时器。
代码
1.基础版
/**
* 防抖函数,在事件触发waitTime毫秒后执行func
* @param func 事件处理函数
* @param waitTime 触发事件与执行处理函数的间隔
* @return {Function} 触发事件调用的JS函数
*/
function debounce(func, waitTime) {
let timer = null; //定时器标识
return function() {
//见代码后解释①
const context = this; //存储this指针,用于绑定setTimeout中的指针指向,这里指向这个函数的调用者。(如果setTimeout中使用箭头函数,则无需存储,直接用)
const args = arguments; //arguments为这个函数接收到的参数,通常为事件对象
if (timer) clearTimeout(timer); //清空上次的定时器。为什么能清空上次的?同代码后解释①
timer = setTimeout( //定时器
function() {
return func.apply(context, args) }, //执行func函数。并且绑定func函数内的this,以及传参数args给func函数。
waitTime //执行func函数等待时间
);
}
}
/*
* 逻辑处理。e视情况需要
*/
function printToConsole(e) {
//e,触发的事件对象
console.log(e);
}
//start resize事件使用
window.onresize = debounce(printToConsole, 1000); //等号'=',先调用了debounce,返回了匿名函数
//上面赋值给window.onresize的JS代码是debounce里return的代码
//即function() {const context = this; ......}。
或
window.addEventListener('resize', debounce(printToConsole, 1000)());
//如果使用addEventListener,则需要手动在debounce后加个'()'去得到返回的匿名函数
//end
解释①:为什么return一个匿名函数?不用行不行?
确保事件触发的是匿名函数,不会重新将timer置为null。同时,匿名函数是一个闭包,timer变量不会被回收。这样,就能使用clearTimeout
清空上次的定时器了。
不能?。在这里,timer会被置为null,清空不了上次的定时器,那样就只是单纯的将所有的逻辑处理函数异步延迟触发了。(可以自己额外维护一个变量?)。
2.在vue
中的使用
当我们将防抖函数写成上面的样子时,如果需要对vue
组件的change事件,watch进行防抖,那么可能就不适用了。如on-change事件:
<sg-input on-change='handleInputValueChange'></sg-input>
handleInputValueChange() {
console.log('hello, changsha!');
}
怎么对change
事件的逻辑处理进行防抖限制呢?
我们知道,change
事件调用的函数始终是匿名函数,而不是整个debounce
函数。
所以我们先调用debounce
并将匿名函数赋值给另一个函数。
methods: {
previewImageDebounce: debounce(this.printToConsole, 1000)
}
//this原因报错 Uncaught TypeError: Cannot read property 'printToConsole' of undefined
这里改成将函数名传到debounce
中,在debounce
中调用逻辑处理函数,如下:
使用:
methods: {
previewImageDebounce: debounce('printToConsole', 1000),
handleInputValueChange() {
this.previewImageDebounce();
}
}
改后的debounce
:
/**
* 防抖函数,在事件触发waitTime毫秒后执行func
* @param func 事件处理函数或者函数名
* @param waitTime 触发与执行处理函数间隔
* @return {Function} 触发事件调用的JS函数
*/
function debounce(func, waitTime) {
let timer = null;
return function() {
const args = arguments;
if (timer) clearTimeout(timer);
+ if (typeof func === 'function') {
+ timer = setTimeout(() => func.apply(this, args), waitTime);
+ } else if (typeof func === 'string') {
+ timer = setTimeout(() => this[func].apply(this, args), waitTime);
+ } else {
+ console.warn('事件处理逻辑未执行');
+ }
}
}
在vue中的使用来自:https://zhuanlan.zhihu.com/p/128055659