节流和防抖都是为了防止某一时间段内事件回调函数频繁触发
防抖 debounce
概念:函数在触发n时间后才执行,如果在时间n内再次触发函数,则n重新计算,可以这么理解:debounce就是法师读条释放技能,如果被打断,就要重新读条
使用场景:
- 按钮的防重复点击
- search搜索联想,用户在不断输入值时,用防抖来节约请求资源
- window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
简单实现
function debounce(fn, delay) {
let timer = null
return function(...args) {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
业务需求中可能需要第一次立即执行的场景
function debounce(fn, delay, immediate) {
let timer = null
return function(...args) {
if(immediate) {
// 第一次执行后取反
immediate = !immediate
fn.apply(this, args)
} else {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
}
const clickFn = debounce((arg) => console.log(arg), 1000, true)
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
clickFn('do some thing')
})
节流 throttle
概念:在n时间内,无论触发多少次,函数最多只会执行一次,可以这么理解:类似fps的射击游戏,无论你点击鼠标多频繁,子弹只会按照固定的射速发射
使用场景:
- 鼠标不断点击触发(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
简单实现(立即执行)
// 定时器实现
function throttle (fn, delay) {
let isRunning = false // 状态位
return function(...args) {
if (!isRunning) {
isRunning = true
fn.apply(this, args)
setTimeout(() => {
isRunning = false
}, delay)
}
}
}
节流函数不止上面一种实现方式,可以使用时间戳来代替状态位来判断时间
时间戳实现
function throttle(fn, delay) {
let lastCall = 0
return function(...args) {
const now = new Date().getTime()
if (now - lastCall > delay) {
fn.apply(this, args)
lastCall = now
}
}
}