逐行解释JS防抖及防抖的使用

概念

防抖就是在事件触发后等待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

猜你喜欢

转载自blog.csdn.net/weixin_44375186/article/details/113986723
今日推荐