函数防抖与节流算是前端性能优化的一个细节点,可能开发大多数是用着 JS 库顺带的防抖节流函数,但我们必须会自己写因为没有必要因为一个函数引入一个库。
这是别人做的一个动画,很容易就能理解区别:防抖与节流
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
function debounce(func, wait, immediate) {
let timeout
function debounced(...args) {
const ctx = this
if (timeout) clearTimeout(timeout) // 每次调用debounce函数都会将前一次的timer清空,确保只执行一次
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
// 这里只有在 wait 时长之后,timeout 为 null,然后触发防抖函数才能立即执行,
// 否则 callNow 为 false,不会立即执行,计时器会重新计时
timeout = null
}, wait)
if (callNow) func.apply(ctx, args)
} else {
timeout = setTimeout(() => {
func.apply(ctx, args)
}, wait)
}
}
debounced.cancel = function cancel() {
// 取消功能
clearTimeout(timeout)
timeout = null
}
return debounced
}
适合多次事件一次响应的情况
- search 搜索联想,用户在不断输入值进行 AJAX 验证时时,用防抖来节约请求资源。
- 按钮点击事件/input 事件,防止用户多次重复提交
- 电话号码输入的验证, 只需停止输入后进行一次。
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
function throttle(func, wait) {
let timeout = 0
return function throttled(...args) {
const ctx = this
if (!timeout) {
timeout = setTimeout(() => {
func.apply(ctx, args)
timeout = null
}, wait)
}
}
}
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 画笔功能
- 游戏中的刷新率