函数节流(throttle)
函数节流:在指定的间隔时间内只执行一次
有个需要频繁触发函数,出于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效。
比如下面的例子,在不加函数节流的时候,每当滚动条滚动的时候都会触发一次,造成大量的性能浪费
// 未添加节流函数
document.onscroll = function () {
console.log('scroll事件被触发了')
}
添加了节流函数后
// 添加了节流函数
document.onscroll = throttle(function () {
console.log('scroll事件被触发了')
}, 300)
具体代码实现
/**
* @description 函数节流
* @param {Function} fn 需要执行函数节流的函数
* @param {Number} interval 指定间隔时间
*/
function throttle(fn, interval = 300) {
let canRun = true // 通过闭包保存一个标记
return function () {
if (!canRun) return // 第一次调用执行
canRun = false // setTimeout未执行时,后续fn函数调用都不会再执行
// setTimeout 定时器延时执行
setTimeout(() => {
fn.apply(this, arguments)
canRun = true // 标记为true,节流完成
}, interval)
}
}
代码解释
原理:将即将被执行的函数用setTimout
延迟一段时间执行,如果该次延迟执行还没有完成,则忽略接下来调用该函数的请求。
简单来说,函数的节流就是通过闭包保存一个标记(canRun = true
), 在函数的开头判断这个标记是否为true
,如果这个标记为true
的话就继续执行,否则就return
掉,判断完标记后立即把这个标记设置为false
,然后把外部传入的函数的执行包在一个setTimout
中,最后在定时器执行完毕之后再把标记设置为true
,表示本次延迟执行完毕,可以执行下一次循环了。当定时器还未执行完毕的时候,canRun
这个标记始终未false
,故在开头的判断中总是被return
掉,函数并未执行
应用场景
监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次等
函数防抖(debounce)
函数防抖: 一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。
比如点击一个按钮,每点击一次就会触发一次事件,在没有加防抖函数的情况下,快速点击会导致多次触发
// 未加防抖函数
document.getElementById('btn').onclick = function(){
console.log('我被点击了');
}
在加了防抖函数后,只会在规定时间后触发一次
// 加了防抖函数
document.getElementById('btn').onclick = debounce(function(){
console.log('我被点击了');
},300)
具体代码实现
/**
* @description 函数防抖
* @param {Function} fn 需要执行函数防抖的函数
* @param {Number} interval 指定间隔时间
*/
function debounce(fn, interval = 300) {
let timeout = null // 通过闭包保存一个标记
return function () {
clearTimeout(timeout) // 把前一个定时器去掉
// 又创建一个新的定时器
timeout = setTimeout(() => {
fn.apply(this, arguments) // 指定的时间间隔之后运行fn
}, interval)
}
}
代码解释
其原理就第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器,然后延迟一定时间再执行。
应用场景
文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)