Skip to main content

性能优化之防抖与节流

防抖与节流是优化高频执行代码的方式,像浏览器的 resizescrollkeypressmousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,造成资源浪费,影响性能,可以用防抖(debounce)和节流(throttle)来减少调用频率。

防抖与节流通俗解释

将大厦电梯每完成一次运送,类比为一次函数的执行和响应,假设 15 秒为电梯超时:

  • 电梯第一个人进来后,等待 15 秒,如果过程中有人进来,则 15 秒等待重新计时,直到 15 秒后开始运送,这是防抖;
  • 电梯第一个人进来后,每 15 秒准时运送一次,这是节流。

一、防抖

n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时。

1、普通防抖

简单实现:

function debounce(func, wait) {
let timer = null;

return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, arguments) // 根据原函数 this 和参数执行函数
timer = null
}, wait);
}
}

2、可立即执行的防抖

防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:

function debounce(func, wait, immediate) {
let timer = null;

return function () {
if (timer) clearTimeout(timer);
if (immediate) {
// 第一次会立即执行,当事件执行后才会再次触发
!timer && func.apply(this, arguments)
timer = setTimeout(() => {
timer = null;
}, wait)
} else {
timer = setTimeout(() => {
func.apply(this, arguments)
timer = null
}, wait);
}
}
}

点击查看 Lodash 防抖方法

二、节流

n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效。

1、时间戳实现

可以使用时间戳与定时器来实现节流,事件会立即执行,停止触发后无法再次执行:

function throttled(fn, delay = 500) {
let oldTime = Date.now()
return function () {
const newTime = Date.now()
if (newTime - oldTime >= delay) {
fn.apply(null, arguments)
oldTime = Date.now()
}
}
}

2、定时器实现

使用定时器写法,delay 毫秒后第一次执行,第二次事件停止触发后会再一次执行:

function throttled(fn, delay = 500) {
let timer = null
return function () {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay);
}
}
}

点击查看 Lodash 节流方法

三、防抖与节流的区别

防抖是在指定时间后执行任务,如果在这期间被重复触发,则重新计时;而节流是指定时间内只执行一次任务。

防抖应用场景:

  • 搜索框搜索输入,等用户最后一次输入完再发送请求;
  • 手机号、邮箱验证输入检测;
  • 窗口大小 resize,等窗口调整完成后再计算窗口大小。

节流应用场景:

  • 滚动加载,加载更多或滚到底部监听;
  • 搜索框,搜索联想功能。