节流防抖

节流 throttle

函数节流指的是某个函数在一定时间间隔内(例如3秒)只执行一次。在这3秒内无视后来产生的函数调用请求

节流可以理解为养金鱼时拧紧水龙头,3秒一滴。

  • 「 管道中的水 」就是我们频繁操作事件而不断涌入的回调任务,它需要接收「 水龙头安排 」。
  • 「 水龙头 」就是节流阀,控制水的流速,过滤无效的回调任务。
  • 「 滴水 」就是每隔一段时间执行一次函数。
  • 「 3秒 」就是间隔时间,它是「 水龙头 」决定「 滴水 」的依据。

两种实现方法

1. 利用时间戳来判断是否已到执行时间,记录上次执行的时间戳,然后每次触发事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经达到时间差,如果是则执行,并更新上次执行的事件戳,如此循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// fn是需要执行的函数
// wait是时间间隔
const throttle = (fn, wait = 50) => {
// 上一次执行的时间戳
let previous = 0
return function(...args){
// 获得当前时间戳
let now = +new Date()
if(now - previous > wait){
previous = now
fn.apply(this, args)
}
}
}

// Demo
// 执行throttle 返回新函数
const betterFn = throttle(()=>{console.log('fn函数执行了')},1000)

// 每10ms执行一次betterFn,但只有时间间隔大于1000ms时才会执行 fn
setInterval(betterFn,100);

通过闭包原理:保存变量previous,在每次调用该闭包时,如果当前时间间隔大于wait,则执行fn,并更新previous

2. 使用定时器,比如当 scroll 事件刚触发时,打印一个 hello world,然后设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。

防抖 debounce

函数防抖指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。

防抖可以理解为司机等待最后一个人进入后再关门,每次新进一个人,司机就会把计时器清零并重新计算。

  • 「 上车的乘客 」就是我们频繁操作事件而不断涌入的回调函数。
  • 「 1分钟 」就是计时器,它是司机决定关门的依据,如果有新的「 乘客 」上车,将清空并重新计时。
  • 「 关门 」就是最后需要执行的函数。

实现

实现原理就是利用定时器,函数第一次执行的时候设定一个计时器,之后调用时发现已经设定过计时器就清空之前的定时器,并重新定义一个新的定时器,如果存在没有被清空的定时器,则当定时器计时结束则执行函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// fn 要执行的函数
// wait 时间间隔
function debounce(fn, wait = 50){
// 通过闭包保存一个定时器timer
var timer = null
return function(...args){
// 如果存在定时器 则清空
if(timer) clearTimeout(timer)

// 重新定义计时器
timer = setTimeout(()=>{
fn.apply(this, args)
},wait)
}
}