一、什么是防抖,为什么使用到防抖?
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
场景:搜索框处理,在需要用到搜索查询的地方,一般都是使用input框加上oninput事件进行处理(获取到值,根据值去请求后端),再比如说:针对滚动条,用户滑动滚动条时,当停止滑动时,便触发某个事件,这一类的情况都可以用到防抖
举个例子:
<body>
<input type="text">
<script>
window.onload = function () {
let inp = document.querySelector('input')
inp.oninput = function () {
console.log(this.value);
}
}
</script>
</body>
/**
*这里有一个知识点,this的指向,在事件内部的this指向是绑定该事件的元素,这里是input绑定了
*oninput事件,那么这里的this指向的就是input框,所以this.value可以拿到输入的值
*/
可以看到,输入好好学习这四个字,这段代码就执行了12次,如果说在输入时需要发送请求呢?执行一次就发送一个请求?那这样一来,输入的关键词太长,请求可能会执行几十上百次,这样一来服务器和浏览器的压力会很大,对用户也是非常不友好的,基于这样的场景,防抖的出现,成功解决了这样的一个问题!
防抖(debounce)
<body>
<input type="text">
<script>
window.onload = function () {
let inp = document.querySelector('input')
let t = null
inp.oninput = function (e) {
if (t != null) {
clearTimeout(t)
}
t = setTimeout(function () {
console.dir(this.value);
}.bind(this), 1000)
}
}
</script>
</body>
/*
*定时器的this指向的是全局的window,那么this.value是取不到input值的,我们可以改变this的指向,从而取到值
*bind()的作用类似call和apply,都是修改this指向。但是call和apply是修改this指向后函数会
立即执行,而bind则是返回一个新的函数,它会创建一个与原来函数主体相同的新函数,新函数中的
this指向传入的对象。
*在这里为什么不能用call和apply,是因为call和apply不是返回函数,而是立即执行函数,那么,就失去了定时器的作用。
*
*/
这里可以看到输入好好学习只执行了一次,这就是防抖
接下来对防抖函数进行封装
<body>
<input type="text">
<script>
window.onload = function () {
let inp = document.querySelector('input')
inp.oninput = debounce(function () {
// 业务代码
console.log(this.value);
}, 1000)
function debounce(fn, delay) {
let t = null
return function () {
if (t != null) {
clearTimeout(t)
}
t = setTimeout(function () {
fn.call(this)
}.bind(this), delay)
}
}
}
</script>
</body>
/*
*大致逻辑是这样的:我们封装时需要考虑业务代码和算法分开,所以oninput事件触发的函数应该在函数内部添加
业务代码,所以防抖函数传如一个回调函数,而我们需要的是延时指向业务代码,所以setTimeout中可以直接执行
传入的fn,为了更加灵活,将时间通过参数的方式传递进来
*在setTimeout的参数函数使用bind改变this的指向,使得指向事件对象,然而我们调用debounce传入的函数的
this指向的是window,所以在调用fn时在用call再一次改变this的指向,使其指向事件对象,就可以拿到value
*这里同时使用到了闭包,debounce中return出去的函数就是一个闭包
*在给input绑定事件的时候没有使用箭头函数,由于箭头函数是没有this的,所以这里使用箭头函数,this指向
在window,无法获取值
*/
debounce(优化)
<body>
<input type="text">
<script>
window.onload = function () {
let inp = document.querySelector('input')
inp.oninput = debounce(function() {
// 业务代码
console.log(this.value);
}, 1000)
function debounce(fn, delay) {
let t = null
return function () {
if (t) clearTimeout(t)
t = setTimeout(()=> {
fn.call(this)
}, delay)
}
}
}
</script>
</body>
二、什么是节流,为什么使用到节流?
节流:连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率
场景:节流同样适用于input搜索,根据业务需要,使用节流防抖均可,在鼠标移动触发某些事件或者滚动触发,请求某些内容,使用节流会更加友好。
举个例子:
<body>
<script>
window.onscroll = function () {
if (flag) {
setTimeout(() => {
console.log('滚动就会执行');
}, 500)
}
}
</script>
</body>
可以看到滚动到末尾,此时没有使用节流,共执行了45次,我们想要的只是在滚动期间,每隔多少事件间隔触发某种事件,并不需要每次都触发,那么这样的一个场景就可以使用到节流了
节流(throttle)
<body>
<script>
let flag = true
window.onscroll = function () {
if (flag) {
setTimeout(() => {
console.log('滚动就会执行');
flag = true
}, 500)
}
flag = false
}
</script>
</body>
这里可以看到,滚动到末尾只触发了2次,这就是节流
throttle(优化)
<body>
<script>
window.onscroll = throttle(function () {
// 业务代码
console.log('滚动就会执行');
}, 500)
function throttle(fn, delay) {
let flag = true
return function () {
if (flag) {
setTimeout(() => {
fn.call(this)
flag = true
}, delay)
}
flag = false
}
}
</script>
</body>
这里节流和防抖封装方法一致,可以参考防抖中的注释,更加容易理解节流