手写防抖(debounce)和节流(throttle)函数
防抖(Debounce)
事件停止触发 n 秒后执行,如果在 n 秒内再次触发则重新计时。
function debounce(fn, delay, immediate = false) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
if (immediate && !timer) {
fn.apply(this, args)
}
timer = setTimeout(() => {
if (!immediate) fn.apply(this, args)
timer = null
}, delay)
}
}
// 使用
input.addEventListener('input', debounce(search, 300))
window.addEventListener('resize', debounce(updateLayout, 200))应用场景:搜索框输入、窗口 resize、表单验证
节流(Throttle)
每 n 秒最多执行一次,在此期间的触发被忽略。
// 时间戳版(第一次立即执行,最后一次不执行)
function throttle(fn, interval) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// 定时器版(第一次不执行,最后一次会执行)
function throttle(fn, interval) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, interval)
}
}
}
// 组合版(第一次和最后一次都执行)
function throttle(fn, interval) {
let lastTime = 0
let timer = null
return function (...args) {
const now = Date.now()
const remaining = interval - (now - lastTime)
if (remaining <= 0) {
if (timer) { clearTimeout(timer); timer = null }
lastTime = now
fn.apply(this, args)
} else if (!timer) {
timer = setTimeout(() => {
lastTime = Date.now()
timer = null
fn.apply(this, args)
}, remaining)
}
}
}应用场景:滚动加载、按钮防重复点击、鼠标拖拽
对比
| 防抖 | 节流 | |
|---|---|---|
| 原理 | 重置计时器 | 固定频率执行 |
| 类比 | 电梯等人 | 电梯定时发车 |
| 场景 | 搜索联想 | 滚动监听 |