JS 中的函数防抖
一、什么是函数防抖?
概念: 函数防抖(debounce), 就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数执行时间。
举个栗子:坐电梯的时候,如果电梯检测到有人进来(触发事件),就会多等待10秒,此时如果又有人进来(10秒之内重复触发事件),那么电梯就会再多等待10秒。
在上述例子中,电梯在检测到有人进入 10 秒钟之后,才会关闭电梯门开始运行,因此,“函数防抖”的关键在于,在 一个事件 发生 一定时间 之后,在执行 特定动作。
二、为什么需要函数防抖?
前端开发过程中,有一些事件,常见的例如:onresize、scroll、mousemove、mousehover 等,会被频繁触发(短时间内多次触发),不做限制的话,有可能一秒之内执行几十次、几百次,如果在这些函数内部执行了其它函数,尤其是执行了操作 DOM 的函数(浏览器操作 DOM 是很耗费性能的),那不仅会浪费计算机资源,还会降低程序运行速度,甚至造成浏览器卡死、崩溃,这种问题显然是致命的。
除此之外,短时间内重复的 ajax 调用不仅会造成数据关系的混乱,还会造成网络拥塞,增加服务器压力,显然这个问题也是需要解决的。
三、函数防抖如何解决上述问题?
根据上面对问题的分析,细细思索,可以想到如下解决方案。
函数防抖的要点,是需要一个 setTimeout 来辅助实现,延迟运行需要执行的代码。如果方法多次触发,则把上次记录的延迟执行代码用 clearTimeout 清掉,重新开始计时。若计时期间事件没有被重新触发,等延迟时间计时完毕,则执行目标代码。
四、函数防抖的代码实现
function debounce(fn, wait) {
let timer = null
return function () {
if (timer !== null) {
clearTimeout(timer)
}
timer = setTimeout(fn, wait)
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('resize', debounce(handle, 1000))
函数的触发方式为 "resize",改变窗口大小,观察浏览器控制台的变化。
五、函数节流的使用场景
函数防抖一般用在什么情况下呢?
一般用在,连续的事件只需要出发一次回调的场合,具体有:
- 搜索框搜索输入。只需要用户最后一次输入完,再发送请求;
- 用户名、手机号、邮箱输入验证;
- 浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。
六、总结
函数防抖其实是分为 “立即执行版” 和 “非立即执行版” 的,根据字面意思就可以发现他们的区别,所谓立即执行版就是 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数;非立即执行版就是触发事件后函数不会立即执行,而是在 n 秒后执行,如果 n 秒内又触发了事件,则会重新计算函数执行时间。
在开发过程中,我们需要根据不同的场景来决定我们需要使用哪一个版本的防抖函数,一般来讲上述的防抖函数都能满足大部分的场景需求,但我们也可将非立即执行版和立即执行版的防抖函数结合起来,实现最终的双剑合璧版本的防抖函数,以下为小伙伴们做了简单的实现:
/**
* @desc 函数防抖 -- “立即执行版本” 和 “非立即执行版本” 的组合版本
* @param fn 需要执行的函数
* @param wait 延迟执行时间(毫秒)
* @param immediate true 表示立即执行, false 表示非立即执行
*/
function debounce(fn, wait, immediate) {
let timer = null
return function () {
let _this = this
let _arguments = arguments
if (timer) {
clearTimeout(timer)
}
if (immediate) {
let callNow = !timer
timer = setTimeout(() => {
timer = null
}, wait)
if (callNow) {
fn.apply(_this, _arguments)
}
} else {
timer = setTimeout(() => {
fn.apply(_this, _arguments)
}, wait)
}
}
}
function handle() {
console.log(Math.random())
}
// 调用立即执行版本
window.addEventListener("mousemove", debounce(handle, 1000, true))
// 调用非立即执行版本
window.addEventListener("mousemove", debounce(handle, 1000, false))
参考链接:https://segmentfault.com/a/1190000019591549,版权归原作者所有。