防抖的需求
我们要解决防抖的问题,第一步要先了解防抖是什么,为什么会产生抖动;
在日常生活中我们经常会需要在表格中输入内容,点击滑块完成验证,scroll事件滚动事件,按钮提交,试想一下如果当我们每一次抬起鼠标向后台发送一次请求,或者移动滑块一次验证一次是不是耗时太久,scroll事件滚动如果碰一下就变化,容易抖动,那么防抖就是为了就是为了解决这个事情
防抖的概念
我们要解决防抖的问题,那么首先要了解防抖的概念
防抖:事件响应函数在一段时间后才执行,如果在这段时间内在次调用,则重新计算执行时间,当预定的时间内没有再次调用该函数,则执行事件响应函数。( 好像当一件事执行后,等待相应时间后才能看到结果,但是如果中途等不及将这件事重新做了一次,又要等待相同的是、时间才能看到结果))
怎么解决防抖
先上一个html和css,方便后续的引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.wrapper {
background-color: #444;
height: 200px;
line-height: 200px;
/* 加入margin方便观察效果 */
margin-left: 80px;
width: 30%;
color: #fff;
text-align: center;
}
</style>
</head>
<body>
<div class="wrapper"></div>
</body>
</html>
这就是一个简单的布局,这里不再做解释.
再给他加一个移动事件以便后期使用
//获取当前的div
let wrapper = document.querySelector('.wrapper');
// 计数
let count = 0;
// 事件
function dosomeThing(e) {
// console.log(e)
console.log(this)
wrapper.innerHTML = count++;
}
wrapper.onmousemove = dosomeThing
控制时间
如果看清了上面的概念,那么我们第一个要解决的问题就是时间的问题,我们要控制在多长时间后再一次触发这件事件,怎么做呢?
function debounce1(func, wait) {
let timer;
return function() {
console.log(this)
clearTimeout(timer); //进来先清除定时器
timer = setTimeout(func, wait)
}
}
//将点击事件引用我们的防抖函数
wrapper.onmousemove = debounce1(dosomeThing, 500)
此时当我们在移入的时候,我们会发现如果不停移动就不会输出,只有停下500ms后才会触发,如下图
我们可以发现下面我们移动了这么多次上面的数据也只加了一,当然这样也就不难发现我们上面打印的this值得问题,所有有了我们的第二步;
改变this指向
你们可能看的不太明显,最后一个打印window的是我们的dosomeThing,上面的div打印来自于debounce1的返回值的第一行,我们肯定希望我们的事件响应函数指向我们的目标元素,那么我们怎么实现呢:
function debounce1(func, wait) {
let timer;
return function() {
// console.log(this)
// 改变执行函数内部this指向问题
// 因为这里的this指向我们的事件源,所以在这里保存this
let that = this;
clearTimeout(timer); //进来先清除定时器
timer = setTimeout(function() {
// 用call方法改变this指向,这里用apply也能达到同样的效果,两者的区别在于传参方式的不同,一个call传入参数列表,apply传入参数数组
func.call(that)
}, wait)
}
这个时候让我们看看控制台的输出结果
这个时候执行函数的this指向问题已经解决,那么我们这里放开我们的console.log(e)看一下结果;
这里输出underfined,我们的MouseEvent消失了,那么下面我们来解决这个问题;
修复MouseEvent
我们可以发现在return内部打印arguments返回的对象有鼠标事件,那么我们在下面将他加进去,保存鼠标事件;
function debounce1(func, wait) {
let timer;
return function() {
// arguments 是一个对应于传递给函数的参数的类数组对象。
console.log(arguments)
// console.log(this)
// 改变执行函数内部this指向问题
// 因为这里的this指向我们的事件源,所以在这里保存this
let that = this;
let argus = arguments;
clearTimeout(timer); //进来先清除定时器
timer = setTimeout(function() {
// 用spply方法改变this指向,这里用call也能达到同样的效果,两者的区别在于传参方式的不同,一个call传入参数列表,apply传入参数数组
// arguments 是一个对应于传递给函数的参数的类数组对象,这里将argus传入,保存event
func.apply(that, argus)
}, wait)
}
这时我们再看一下执行函数的console.log(e)
正常
那么到这里基本就已经完成了,封装后续使用,欢迎取用
/**
* 防抖函数
* @param {Function} func 执行的响应函数
* @param {Number} wait 等待时间
*/
function debounce1(func, wait) {
let timer;
return function() {
let that = this;
let argus = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
func.apply(that, argus)
}, wait)
}
}