0%

手写防抖与节流函数

函数防抖与节流算是前端性能优化的一个细节点,可能开发大多数是用着 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) {
// this保存给context
const context = this
// 每次调用debounce函数都会将前一次的timer清空,确保只执行一次
if (timer) clearTimeout(timer)

// immediate 为 true 表示第一次触发后执行
// 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 画笔功能
  • 游戏中的刷新率
-------------本文结束感谢您的阅读-------------