共计 2017 个字符,预计需要花费 6 分钟才能阅读完成。
曾经面试时候被问到过这个,年少的我一脸无知。。。
后来工作中遇到了一个场景:输入名称的同时去服务器校验名称是否重复,但发现之前的代码竟然都没做限制,输入一次发一次请求。简直忍不了,就在项目的 utils 里加上了防抖函数。
正好做一个总结,加深印象。
函数防抖和节流,都是控制事件触发频率的方法。应用场景有很多,输入框持续输入,将输入内容远程校验、多次触发点击事件、onScroll 等等。
为了说明问题,假设一个场景:鼠标滑过一个 div,触发 onmousemove 事件,它内部的文字会显示当前鼠标的坐标。
<style>
#box {
width: 1000px;
height: 500px;
background: #ccc;
font-size: 40px;
text-align: center;
line-height: 500px;
}
</style>
<div id="box"></div>
<script>
const box = document.getElementById('box')
box.onmousemove = function (e) {box.innerHTML = `${e.clientX}, ${e.clientY}`
}
</script>
效果是这样的:
在上边的场景下,我们不希望触发一次就执行一次,这就要用到防抖或节流。下面我们看一下它们能为我们做什么吧。
防抖
函数防抖,这里的抖动就是执行的意思,而一般的抖动都是持续的,多次的。假设函数持续多次执行,
我们希望让它冷静下来再执行。也就是当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的
一段时间之后,再去执行。先看一下效果:
分解一下需求:
- 持续触发不执行
- 不触发的一段时间之后再执行
那么怎么实现上述的目标呢?我们先看这一点:在不触发的一段时间之后再执行,那就需要个定时器呀,定时器里面调用我们要执行的函数,将 arguments 传入。
封装一个函数,让持续触发的事件监听是我们封装的这个函数
function debounce(func, delay) {return function() {setTimeout(() => {func.apply(this, arguments)
}, delay)
}
}
第二点实现了,再看第一点:持续触发不执行。我们先思考一下,是什么让我们的函数执行了呢?是上边的 setTimeout。OK,那现在的问题就变成了
持续触发,不能有 setTimeout。这样直接在事件持续触发的时候,清掉定时器就好了。
function debounce(func, delay) {
let timeout
return function() {clearTimeout(timeout) // 如果持续触发,那么就清除定时器,定时器的回调就不会执行。timeout = setTimeout(() => {func.apply(this, arguments)
}, delay)
}
}
用法:
box.onmousemove = debounce(function (e) {box.innerHTML = `${e.clientX}, ${e.clientY}`
}, 1000)
节流
节流的意思是让函数有节制地执行,而不是毫无节制的触发一次就执行一次。什么叫有节制呢?就是在一段时间内,只执行一次。
同样,我们分解一下:
- 持续触发并不会执行多次
- 到一定时间再去执行
效果是这样的:
思考一下,持续触发,并不会执行,但是到时间了就会执行。抓取一个关键的点:就是执行的时机。
要做到控制执行的时机,我们可以通过一个开关,与定时器 setTimeout 结合完成。
函数执行的前提条件是开关打开,持续触发时,持续关闭开关,等到 setTimeout 到时间了,再把开关打开,函数就会执行了。
我们看一下代码怎么实现:
function throttle(func, deley) {
let run = true
return function () {if (!run) {return // 如果开关关闭了,那就直接不执行下边的代码}
run = false // 持续触发的话,run 一直是 false,就会停在上边的判断那里
setTimeout(() => {func.apply(this, arguments)
run = true // 定时器到时间之后,会把开关打开,我们的函数就会被执行
}, deley)
}
}
调用的时候:
box.onmousemove = throttle(function (e) {box.innerHTML = `${e.clientX}, ${e.clientY}`
}, 1000)
这样,就实现了节流,节流还可以用时间间隔去控制,就是记录上一次函数的执行时间,与当前时间作比较,如果当前时间与上次执行时间的时间差大于一个值,就执行。
总结
防抖和节流巧妙地用了 setTimeout,来控制函数执行的时机,优点很明显,可以节约性能,不至于多次触发复杂的业务逻辑而造成页面卡顿。
欢迎关注我的公众号:一口一个前端,不定期分享我所理解的前端知识