专题 | 防抖和节流

防抖:单位时间内,频繁触发事件,只执行最后一次

          场景:搜索框搜索输入(利用定时器,每次触发先清掉以前的定时器,从新开始)

节流:单位时间内,频繁触发事件,只执行一次

         场景:高频事件 快速点击,鼠标滑动、resize事件、scroll事件(利用定时器,等定时器执行完毕,才开启定时器,不用打断)

                  一般用lodash库,利用里面的debounce(防抖)和throttle(节流)来做

【前端面试题:防抖与节流(二)】 https://www.bilibili.com/video/BV1ig411u7LG/?share_source=copy_web&vd_source=c1fe9c75396fdc6f65b56d15f5eb00b3

防抖

防抖:用户触发事件过于频繁,只需要最后一次事件的操作

案例一:输入框触发过于频繁 

只想要暴富两字,前面哪些过程不想要把它输出来

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text">
		<script>
		let inp = document.querySelector("input");
		inp.oninput = function(){
			console.log(this.value);
		}
		</script>
	</body>
</html>

案例二:用定时器解决触发频繁问题

差不多解决了,可以在调整一下时间更完美

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text">
		<script>
		// 防抖方法一  好烦啊,我也不知道是哪些部分不懂,明明拆开我全晓得,烦躁
		 let inp = document.querySelector("input");
		 // t代表的是用户触发的次数
		 let t = null;
		 inp.oninput = function(){
		 	if(t !== null){
				clearTimeout(t);
			}
			t = setTimeout(()=>{
				console.log(this.value);
			},500)
		}
		
		</script>
	</body>
</html>

案例三:用debounce解决触发频繁问题

案例二的代码据说是一团垃圾,因为业务逻辑和什么混在一起了,所以下面用闭包封的更好些

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<input type="text">
		<script>
			// 防抖方法二 debounce
		let inp = document.querySelector("input");
		let t = null;
		inp.oninput = debounce(function(){
			console.log(this.value);
		},500)
		
		function debounce(fn,delay){
			return function(){
				if(t !== null){
					clearTimeout(t);
				}
				t = setTimeout(()=>{
					fn.call(this);
				},delay)
			}
		}
		
		</script>
	</body>
</html>

节流

节流就是控制执行次数

案例一:执行次数过多 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			body{
				height:800px;
			}
		</style>
	</head>
	<body>
		<script>
			window.onscroll = function(){
			 	console.log("123");
			}
		</script>
	</body>
</html>

案例二:定时器节流

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			body{
				height:800px;
			}
		</style>
	</head>
	<body>
		<script>
		// 节流方法一 定时器
		let flag = true;
		window.onscroll = function(){
			if(flag){
				setTimeout(()=>{
					console.log("111");
					flag = true;
				},500)
			}
			flag = false;
		}
		</script>
	</body>
</html>

案例三:throttle节流

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			body{
				height:800px;
			}
		</style>
	</head>
	<body>
		<script>
		// 节流方法二:throttle
		let flag = true;
		window.onscroll = throttle(function(){
			console.log("111");
		},500)
		function throttle(fn,delay){
			let flag = true;
			return function(){
				if(flag){
					setTimeout(()=>{
						fn.call(this)
						flag = true
					},delay)
				}
				flag = false;
			}
		}
		</script>
	</body>
</html>

【手写函数防抖和节流】 https://www.bilibili.com/video/BV1pQ4y1M71e/?p=3&share_source=copy_web&vd_source=c1fe9c75396fdc6f65b56d15f5eb00b3

防抖

事件响应函数在一段时间后才执行,如果在这段事件内再次调用,则重新计算执行时间;当预定的时间内没有再次调用,则执行它

防抖的应用场景:scroll事件滚动触发

                             搜索框输入查询

                             表单验证

                             按钮提交事件

                             浏览器窗口缩放,resize事件

 案列:没有使用防抖时

没有使用防抖前

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			#container{
				width:100%;
				height:200px;
				line-height: 200px;
				text-align: center;
				color:#fff;
				background-color: #444;
				font-size: 30px;
			}
		</style>
	</head>
	<body>
	 <div id="container"></div>
	 <script>
		 let count = 0;
		 let container = document.querySelector("#container");
		 function dosomeThing(){
			 container.innerHTML = count++;
		 }
		 
		 container.onmousemove = dosomeThing;
	 </script>
	</body>
</html>

 案例2:使用防抖,在一段时间内执行一次

1s执行一次

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			#container{
				width:100%;
				height:200px;
				line-height: 200px;
				text-align: center;
				color:#fff;
				background-color: #444;
				font-size: 30px;
			}
		</style>
	</head>
	<body>
	 <div id="container"></div>
	 <script>
		 // 自己封装一个防抖函数
		 function debounce(func,wait){
			 let timeout;
			 return function(){
				 clearTimeout(timeout);
				 timeout = setTimeout(func,wait);
			 }
		 }
		 
		 
		 let count = 0;
		 let container = document.querySelector("#container");
		 function dosomeThing(){
			 container.innerHTML = count++;
		 }
		 // 这里进行了封装函数的调用debounce
		 container.onmousemove = debounce(dosomeThing,1000);
	 </script>
	</body>
</html>

案列3:使用防抖,同时解决this指向和event事件指向问题,再添加一个立即执行参数

这里的代码就不太懂了

我看不出来这添加立即执行效果的区别

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			#container{
				width:100%;
				height:200px;
				line-height: 200px;
				text-align: center;
				color:#fff;
				background-color: #444;
				font-size: 30px;
			}
		</style>
	</head>
	<body>
	 <div id="container"></div>
	 <script>
		 // 自己封装一个防抖函数
		 function debounce(func,wait,immediate){
			 let timeout;
			 return function(){
				 // 改变this指向
				 let context = this;
				 // 解决e的指向问题
				 let args = arguments;
				 clearTimeout(timeout);
				 // 立即执行
				 if(immediate){
					 let callNow = !timeout;
					timeout = setTimeout(()=>{
						 timeout = null
					 },wait);
					if(callNow) func.apply(context,args)
				 }else{
					timeout = setTimeout(function(){
							 func.apply(context);
					},wait); 
				 }
				 
			 }
		 }
		 
		 
		 let count = 0;
		 let container = document.querySelector("#container");
		 // 事件event/e的指向
		 function dosomeThing(e){
			 // console.log(this);
			 // console.log(e);
			 container.innerHTML = count++;
		 }
		 // 这里进行了封装函数的调用debounce
		 container.onmousemove = debounce(dosomeThing,500,true);
	 </script>
	</body>
</html>

案列4:增加一个取消防抖按钮,返回函数返回值

这个代码我就更不懂了

点击取消防抖按钮取消防抖效果

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			#container{
				width:100%;
				height:200px;
				line-height: 200px;
				text-align: center;
				color:#fff;
				background-color: #444;
				font-size: 30px;
			}
		</style>
	</head>
	<body>
	 <div id="container"></div>
	 <button id="btn">取消防抖</button>
	 <script>
		 // 自己封装一个防抖函数
		 function debounce(func,wait,immediate){
			 let timeout,result;
			 let decounced =  function(){
				 // 改变this指向
				 let context = this;
				 // 解决e的指向问题
				 let args = arguments;
				 clearTimeout(timeout);
				 // 立即执行
				 if(immediate){
					 let callNow = !timeout;
					timeout = setTimeout(()=>{
						 timeout = null
					 },wait);
					if(callNow) result = func.apply(context,args)
				 }else{
					timeout = setTimeout(function(){
							 func.apply(context);
					},wait); 
				 }
				 return result;
			 }
			 decounced.cancel = function(){
				 clearTimeout(timeout);
				 timeout = null;
			 }
			 return decounced;
		 }
		 
		 
		 let count = 0;
		 let container = document.querySelector("#container");
		 // 事件event/e的指向
		 function dosomeThing(e){
			 // console.log(this);
			 // console.log(e);
			 container.innerHTML = count++;
		 }
		 
		 let doSome = debounce(dosomeThing,800);
		 
		 btn.onclick =  function(){
			 doSome.cancel()
		 }
		 
		 // 这里进行了封装函数的调用debounce
		 container.onmousemove = doSome;
	 </script>
	</body>
</html>

节流 throttle

如果你持续触发事件,每隔一段时间,只执行一次事件

这个笔记记得我自己都蒙了,没有一下记完,搞不懂当初的自己想要怎么记它。

应用场景:

        DOM元素的拖拽

        射击游戏

        计算鼠标移动的距离

        监听scroll滚动事件

案列一:借助别人写好的throttle函数 


<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			container.onmousemove = _.throttle(doSomeThing,2000,{
				// 第一次禁止执行,最后一次也是
				leading:false,
				trailing:false
			});;
		</script>
	</body>
</html>

 案列二:使用时间戳实现节流

鼠标移进去立即执行,隔两秒再执行,离开不执行

 

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			function throttle(func,wait){
				let context,args;
				// 之前的时间戳
				let old = 0;
				// let previous = 0;
				return function(){
					context = this;
					args = arguments;
					// 获取当前的时间戳
					let now = new Date().valueOf();
					if(now-old>wait){
						// 立即执行
						func.apply(context,args);
						old = now;
					}
				}
			}
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			container.onmousemove = throttle(doSomeThing,2000);
		</script>
	</body>
</html>

案列三:使用定时器实现节流

第一次不会触发,最后一次会触发

 

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			function throttle(func,wait){
				let context,args,timeout;
				//
				
				return function(){
					context = this;
					args = arguments;
					if(!timeout){
						setTimeout(()=>{
						  timeout = null;
						  func.apply(context,args);
						},wait)
					}
				}
			}
		}
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			container.onmousemove = throttle(doSomeThing,2000);
		</script>
	</body>
</html>

案列4:时间戳和定时器一起使用实现节流

第一次上来就执行,最后一次离开执行

下面两个代码效果一样,不过进行了一些顺序改动,希望你好好看看分析分析

 

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			function throttle(func,wait){
				let context,args,timeout;
				// 时间戳
				let old =0;
				
				return function(){
					context = this;
					args = arguments;
					
					let now = new Date().valueof();
					if(now-old > wait){
						if(timeout){
							clearTimeout(timeout);
							timeout = null;
						}
						func.apply(context,args);
						old = now;
					}
					if(!timeout){
					timeout =setTimeout(()=>{
						old = new Date().valueOf();
						  timeout = null;
						  func.apply(context,args);
						},wait)
					}
				}
			}
		}
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			container.onmousemove = throttle(doSomeThing,2000);
		</script>
	</body>
</html>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			function throttle(func,wait){
				let context,args,timeout;
				// 时间戳
				let old =0;
				// 改动代码进行了一个封装
				let later = function(){
					old = new Date().valueOf();
					  timeout = null;
					  func.apply(context,args);
				}
				return function(){
					context = this;
					args = arguments;
					
					let now = new Date().valueOf();
					if(now-old > wait){
						if(timeout){
							clearTimeout(timeout);
							timeout = null;
						}
						func.apply(context,args);
						old = now;
					}else if(!timeout){
					timeout = setTimeout(later,wait)
					}
			}
		}
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			container.onmousemove = throttle(doSomeThing,2000);
		</script>
	</body>
</html>

代码优化,几种一个代码实现

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
		#container{
			width:100%;
			height:200px;
			line-height: 200px;
			text-align: center;
			color:#fff;
			background-color: #444;
			font-size: 30px;
		}
		</style>
	</head>
	<body>
		<div id="container"></div>
		<script>
			function throttle(func,wait,options){
				let context,args,timeout;
				// 时间戳
				let old =0;
				if(!options) options = {};
				let later = function(){
					old = new Date().valueOf();
					  timeout = null;
					  func.apply(context,args);
				}
				return function(){
					context = this;
					args = arguments;
					
					let now = new Date().valueOf();
					if(options.leading === false && !old){
						old = now;
					}
					if(now-old > wait){
						if(timeout){
							clearTimeout(timeout);
							timeout = null;
						}
						func.apply(context,args);
						old = now;
					}else if(!timeout && options.trailing !== false){
					timeout = setTimeout(later,wait)
					}
			}
		}
			let count = 0;
			// 演示事件是如何频繁发生的
			let container = document.querySelector("#container");
			let btn = document.querySelector("#btn");
			function doSomeThing(e){
				// event指向问题
				console.log(e);
				// 改变执行函数内部this的指向
				console.log(this);
				// 可能会做回调或者ajax请求
				container.innerHTML = count++;
				return '想要的结果'
			}
			// 要变成第几种情况,注意改下面的leading和trailing的值就行
			 // 现在是第二种代码情况怎么写
			 container.onmousemove = throttle(doSomeThing,2000,{
			leading:false,
			trailing:true
			 });
		</script>
	</body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值