函数防抖与节流算是前端性能优化的一个细节点,可能开发大多数是用着 JS 库顺带的防抖节流函数,但我们必须会自己写因为没有必要因为一个函数引入一个库。
防抖与节流效果
这是别人做的一个动画,很容易就能理解区别:防抖与节流
函数防抖(debounce)
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function debounce(fn, wait = 50, immediate = false) { let timer = null
function debounced(...args) { const context = this if (timer) clearTimeout(timer)
if (immediate && !timer) { fn.apply(context, args) }
timer = setTimeout(() => { fn.apply(context, args) }, wait) }
debounced.cancel = function cancel() { clearTimeout(timer) timer = null }
return debounced }
|
应用场景
适合多次事件一次响应的情况
- search 搜索联想,用户在不断输入值进行 AJAX 验证时时,用防抖来节约请求资源。
- 按钮点击事件/input 事件,防止用户多次重复提交
- 电话号码输入的验证, 只需停止输入后进行一次。
函数节流(throttle)
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
定时器版
1 2 3 4 5 6 7 8 9 10 11 12
| function throttle(func, wait) { let timeout = null return function throttled(...args) { const ctx = this if (!timeout) { timeout = setTimeout(() => { func.apply(ctx, args) timeout = null }, wait) } } }
|
时间戳节流
1 2 3 4 5 6 7 8 9 10 11 12
| function throttle(func, wait) { let previous = 0 return function throttled(...args) { const ctx = this const now = Date.now() const remain = wait - (now - previous) if (remain <= 0) { func.apply(ctx, args) previous = now } } }
|
应用场景
适合大量事件按时间做平均分配触发。
- 监听滚动或 resize 事件,比如是否滑到底部自动加载更多,调整窗口大小用 throttle 来判断
- DOM 元素拖拽
- 自动保存草稿功能,当用户在输入的时候(一直触发事件),单位时间内只保存一次草稿
- Canvas 画笔功能
- 游戏中的刷新率