防抖与节流(小白学习防抖节流篇)

为什么要使用防抖、节流?

开发中经常遇到一些需要频繁触发的事件,例如window对象的视口大小改变、滚动(resize,scroll)、鼠标移动事件(mousedown,mousemove等),表单标签的键盘按下弹起事件(keydown,keyup)等等;

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text" name="" id="" value="" />
	</body>
	<script type="text/javascript">
		document.querySelector("input").addEventListener("keyup", e => {
			console.log(e.target.value)
		})
	</script>
</html>

 

 又比如,我们在事件触发后需要请求数据,这样多频率的请求数据显然是不可取的,为了规避类似事件的频繁触发,我们可以使用防抖,节流。

防抖

原理:

事件触发n秒后再去执行回调函数,若在n秒内事件再次触发,则时间重新计时,实质是闭包。结果就是将频繁触发的事件合并为一次,且在最后执行。

代码实现:

初步实现:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text" name="" id="" value="" />
	</body>
	<script type="text/javascript">
		
		function debounce(callback,wait){
			let timer;
			console.log(this)
			return () => {
				clearTimeout(timer);
				timer = setTimeout(() => {
					callback()
				},wait)
			}
		}
		
		function callback(){
			console.log('回调函数执行了');
		}
		
		document.querySelector("input").addEventListener("keyup",debounce(callback,1000))
		
	</script>
</html>

但是开发中几乎都是需要从目标节点上获取信息执行回调,比如输入用户名查询用户,需要获取输入的用户名发请求查询。可以用apply(apply详解)改变回调函数的this指向,从而获取节点信息,并且通过实参集合(arguments)把目标节点的事件对象也传入到回调函数中,保证回调函数中也可拿到事件对象操作。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text" name="" id="" value="" />
	</body>
	<script type="text/javascript">
		
		function debounce(callback,wait){
			let timer;
			return function(){
				clearTimeout(timer);
				timer = setTimeout(() => {
					callback.apply(this,arguments)
				},wait)
			}
		}
		
		function callback(e){
			console.log('回调函数执行了');
			console.log(e);
			console.log(this);
		}
		
		document.querySelector("input").addEventListener("keyup",debounce(callback,1000))
		
	</script>
</html>

这样就完美了吗?非也非也。倘若有些需求是在事件触发后回调函数立即执行,比如滚动加载,当触底后则立即执行回调。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text" name="" id="" value="" />
	</body>
	<script type="text/javascript">
		
		function debounce(callback,wait,immediate){
			let timer;
			return function(){
				clearTimeout(timer);
				if(immediate){
					let callNow = !timer;
					timer = setTimeout(() => {
						timer = null;
					},wait)
					//立即执行
					if(callNow) callback.apply(this,arguments);
				}else{
					timer = setTimeout(() => {
						callback.apply(this,arguments)
					},wait)
				}
				
				
			}
		}
		
		function callback(e){
			console.log('回调函数执行了');
			console.log(e);
			console.log(this);
		}
		
		document.querySelector("input").addEventListener("keyup",debounce(callback,2000,true))
		
	</script>
</html>

 

最后就是添加取消防抖功能:

function debounce(callback,wait,immediate){
	let timer;
	return function(){
		clearTimeout(timer);
		if(immediate){
			let callNow = !timer;
			timer = setTimeout(() => {
				timer = null;
			},wait)
			//立即执行
			if(callNow) result = callback.apply(this,arguments);
		}else{
			timer = setTimeout(() => {
				callback.apply(this,arguments)
			},wait)
		}
	}
	debounce.cancel = () => {
		clearTimeout(timer);
		timer = null
	}
}

防抖就到这了,接下来说说节流吧。

节流

原理:

在频繁触发事件中,间隔n秒执行一次回调函数。

代码实现:

节流有两种实现方式,用时间差和定时器。

	// 时间差实现
	function throttle(callback, wait) {
		let oldtime = 0;
		return function() {
			let now = new Date().getTime();
			if (now - oldtime > wait) {
				callback();
				oldtime = now
			}
		}
	}
	// 定时器实现
	function throttleTimeOut(callback, wait) {
		let timeOut;
		return function() {
			if(!timeOut){
				timeOut = setTimeout(() => {
					timeOut = null;
					callback.apply(this,arguments);
				},wait)
			}
		}
	}
	function fn(e) {
		console.log(this.value)
		console.log(e);
	}
	document.querySelector("input").addEventListener("keyup", throttleTimeOut(fn, 1000))

两种方法实现的节流,用时间差实现的节流是立即执行最后一次不执行(顾头不顾尾),定时器实现的则是不立即执行,最后一次执行(顾尾不顾头);两者结合就可以得到顾头顾尾的节流函数:

	function throttle(callback, wait) {
		let timeOut;
		let oldtime = 0;
		return function() {
			
			let now = new Date().getTime();
			if (now - oldtime > wait) {
				if(timeOut){
					clearTimeout(timeOut);
					timeOut = null;
				}
				callback.apply(this,arguments);
				oldtime = now
			}
			
			if(!timeOut){
				timeOut = setTimeout(() => {
					oldtime = new Date().getTime()
					timeOut = null;
					callback.apply(this,arguments);
				},wait)
			}
			
			
		}
	}

 

参考博客:js 防抖实战讲解

猜你喜欢

转载自blog.csdn.net/m0_43599959/article/details/114777356