防抖(debounce)
定义
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
应用场景
- 搜索框输入发送AJAX请求
- 窗口缩放,每次resize/scroll触发事件
用例
比如有一个关键词搜索的input框,用户输入后触发keyup事件向后端发送一个请求:
<div>
<input id="input" type="text">
</div>
<script>
const input = document.getElementById('input')
input.addEventListener('keyup', (e) => {
console.log(e.target.value)
})
</script>
测试后可以发现,每次输入都会有打印结果,如果是请求后端接口,那么不仅会造成极大的浪费,而且实际应用中,用户也是输出完整的字符后,才会请求。下面我们优化一下:
function debounce(fn, delay) {
let timer = null
return function(...args) {
const context = this
if(timer) {
clearTimeout(timer)
}
const _agrs = args.join(',')
timer = setTimeout(fn.bind(context, _agrs), delay)
}
}
const consoleDebounce = debounce(console.log, 500)
const input = document.getElementById('input')
input.addEventListener('keyup', (e) => {
consoleDebounce(e.target.value)
})
在运行一次后可以看到,当在频繁的输入时,并不会打印结果,只有当你在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。
完整实现一个通用防抖函数
function debounce(fn, delay = 500, immediate = false) {
let timer = null
return function(...args) {
const context = this
// 是否立即执行一次
if (immediate && !timer) {
fn.apply(context, args)
}
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(context, args)
}, delay)
}
}
节流(throttle)
定义:
指连续触发事件但是在 n 秒中只执行一次函数。 节流会稀释函数的执行频率
应用场景
- DOM 元素的拖拽功能实现(mousemove)
- 计算鼠标移动的距离(mousemove)
- 监听滚动事件判断是否到页面底部自动加载更多内容
- 按钮点击事件(多次点击 n 秒内只生效一次)
用例
一个 button 按钮,每次点击需要提交表单。
<div>
<button id="button">提交</button>
</div>
<script>
const button = document.getElementById('button')
button.addEventListener('click', (e) => {
console.log('click')
})
</script>
上面代码的问题是,当我们快速点击 button 时,每次点击都会打印 'click',下面用节流函数优化一下。
const conThrottle = throttle(console.log, 2000)
const button = document.getElementById('button')
button.addEventListener('click', (e) => {
conThrottle(1)
})
function throttle(fn, time) {
let timer = null
let flag = true
return function(...args) {
let context = this
if (flag) {
fn.apply(context, args)
flag = false
timer = null
}
if (!timer) {
timer = setTimeout(() => {
flag = true
}, time)
}
}
}
再次测试,可以发现不管我们点击的速度多么快,在每 2 秒内,函数只会执行 1 次。
完整实现时间戳版本 throttle 函数
function throttle(fn, time) {
let startTime = 0
return function(...args) {
let context = this
let endTime = Date.now()
if (endTime - startTime >= time) {
fn.apply(context, args)
startTime = endTime
}
}
}