js之防抖篇【渐进式】

在开发中经常碰到需要基于监听滚动条、窗口放大缩小、鼠标移动的需求,应该都能发现如果不加防抖或是节流函数,会使得这个监听器频繁的触发,可能会导致失帧的情况
下面会一步步的渐进式的去实现防抖功能,原理其实很简单

  • 页面:
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0px;
				padding: 0px;
			}
			#box{
				width: 100%;
				line-height: 30vh;
				background-color: #B7ACAC;
				text-align: center;
			}
		</style>
	</head>
	<body>
		<div id="box">
			0
		</div>
	</body>
</html>

先看一下没使用防抖时触发的频率
  • js:
<script>
	var num = 0;
	var dom = document.getElementById("box");
	function mouse() {
		num ++;
		dom.innerText = num;
	}
	
	dom.addEventListener("mousemove", mouse);
</script>

在这里插入图片描述
可以看到触发的频率是很频繁的

接下来按着需求一步步实现防抖函数

第一步实现 ,当鼠标移动时无论怎么移动都不触发,停止移动一秒后再触发

dom.addEventListener("mousemove", debounce(mouse, 1000));
			function debounce(func, wait) {
				var timer;
				return function () {
					clearTimeout(timer)
					timer = setTimeout(func, wait);
				}
			}

这一步应该很好理解,利用闭包缓存timer变量,每次触发都会生成一个计时器任务使用timer变量保存定时器的ID并且清除这个任务,停止移动时就只剩下一个定时器任务
看下效果,很赞
在这里插入图片描述

立即执行版的防抖函数

这个版本就是我一移动就直接执行函数,继续移动是不管用的,必须要停下一定的时间是再移动才触发
可能有一些提交表单的按钮也会需要用到,按了之后立即提交表单验证,再触发必须要等一段时间,当然有验证的话按完清空表单或者禁用按钮都是可选的

dom.addEventListener("mousemove", debounce(mouse, 1000));
			function debounce(func, wait) {
				var timer;
				return function () {
					if(timer) clearTimeout(timer);
					if(!timer) func();
					timer = setTimeout(() => {
						timer = null;
					},wait)
				}
			}

这里的执行原理:

  1. 当第一次执行防抖函数if(timer) clearTimeout(timer);这时timer还是undefined 所以不会执行清除语句
  2. if(!timer) func();第一次执行timer是undefined在前面加上逻辑非!就变为true直接执行该语句,也就是执行函数
  3. 执行定时器并给 timer赋值,这个值是定时器任务的id,先说鼠标一直移动的情况:

当鼠标一直移动时,timer是有值的,第二次触发if(timer) clearTimeout(timer);会取消对应的定时器任务,这里需要注意的一点是虽然任务被取消但是timer变量保存的还是该任务的id。
所以不会执行if(!timer) func();,因为timer是有值的
接下来继续添加计时器任务,并给timer变量赋上相对应的任务id

再说一下鼠标不移动时的执行情况

当鼠标不在移动的情况下,这是定时器任务执行下一个,这个任务会在指定的时间下给timer赋值null
再次移动是就会像上面说的一样立即执行

看下效果,是我们要的没错了
在这里插入图片描述

结合一下两个模式,根据传第三个参数来按条件执行
dom.addEventListener("mousemove", debounce(mouse, 1000, false));
function debounce(func, wait, immediate) {
	var timer;
	return function () {
		if(timer) clearTimeout(timer);
		if(immediate){
			if(!timer) func();
			timer = setTimeout(() => {
				timer = null;
			},wait)
		}else{
			timer = setTimeout(func, wait);
		}
	}
}

是不是超级简单

绑定this,event

但接下来还有问题就是this的指向、event对象问题,现在的this是指向window的,现在我们来改变一下this指向和event对象,肯定是使用apply啦

function mouse(e) {
	num ++;
	dom.innerText = num;
	console.log(this)
	console.log(e.target)
}

dom.addEventListener("mousemove", debounce(mouse, 1000, false));
function debounce(func, wait, immediate) {
	var timer;
	return function () {
		var that = this, ags = arguments;
		if(timer) clearTimeout(timer);
		if(immediate){
			if(!timer) func.apply(that, ags);
			timer = setTimeout(() => {
				timer = null;
			},wait)
		}else{
			timer = setTimeout(() => {
				func.apply(that, ags)
			}, wait);
		}
	}
}

这样就大功告成了,如果还需要其他的自己也知道该怎么加了,apply会立即执行函数的哟
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值