【js手写】防抖和节流函数

防抖和节流

1.防抖

1.1 防抖的概念

防抖:当某个事件触发的频率过高时,为了避免不必要的性能开销,我们规定这个事件触发后,需要等待一段时间再执行其绑定的逻辑代码,如果在这一段时间内,该事件没有再次触发,就执行这个事件对应的逻辑,否则就重新进行计时等待,确保该事件不被过于频繁的触发。 比如我们为鼠标的移动绑定一个事件,显然这个事件会被过于频繁的触发,势必会带来很大的开销,甚至影响到UI的渲染,导致页面卡顿。

1.2 实现思路

  1. 构建一个debouce函数,这个函数中保存一个timeout变量用于记录设置的定时器状态,利用这个变量用于判断该事件的触发状态。
  2. 设置计时器来控制函数触发,并设置判断逻辑:

- 当前对应逻辑函数首次触发,那么我们为其设置定时器,并将其保存到timeout变量 - 当对应逻辑函数已经进入计时阶段,那么我们在还没有逻辑函数触发前,清除设置过的定时器,并重新定时器进行定时

1.3 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 防抖: 一个函数 在设置的时间后执行 如果在设置的时间间隔期间再次触发 那么本次就无效 重新计算
// 触发高频时间后n秒内只会执行一次 如果n秒高频时间内再次触发 则会重新计算时间
// 原理 debounce 触发后 首先清除掉timeout(释放 指向空) 然后返回节流数组 利用闭包保存timeout变量
const debounce = (fn, time) => {
// 利用闭包避免全局污染
let timeout = null;
return function () {
if (timeout) {
// 在定时期间,那么清除原来计时器 重新计时(核心)
clearTimeout(timeout);
}
// 设置定时器
timeout = setTimeout(() => {
// 执行函数
fn.apply(this, arguments)
}, time);
}
}
function clickButton(type) {
console.log(type)
}
//注意要绑定的事件一定是经过debouce处理过的事件,另外不能直接绑定debouce(clickButton,1000)('防抖') 因为这样绑定的函数都是重新在堆里开辟的新函数,每个都会创建新的timeout(不再是闭包中被保护的timeout)
const bindClick = debounce(clickButton, 1000);

html部分:

2.节流

2.1 节流的概念

节流和上述防抖的思路类似,它们的不同点是,防抖在事件一直触发的时候是不会执行逻辑的(必须等待对应时间不再次触发事件),而节流则是按照规定时间,必定再次触发。 打个比方: - 防抖的场景:小谢催小尹还钱,小尹决定三天后还小谢的钱,但是小谢催款的话,小尹就重新计算这个三天的期限,20号催款,23还,21号再催,就24号还。除非小谢这三天没有催小尹还钱。 - 节流的场景:小尹催小谢还钱,小谢决定三天内还小尹的钱,就算小尹这三天怎么催,小谢都会在第一次听到要还钱后的第三天,把钱还了。20号第一次催款,不论21,22号有没有催,小谢都会在23号把钱还给小尹。(小谢真好)

2.2 实现思路

  • 与防抖类似,但是这次我们换用一个闸门:status来保存该事件是否已经触发。如果没有触发,那就设置定时器,将其闸门标记修改,如果已经触发,那就直接返回,不进行操作。

2.3 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 节流 :重新触发不影响 原计时器 
const throttle = (fn, time) => {
// 利用闭包 保存当前激活状态
let status = false;
return function () {
if (status) {
return;
}
else {
status = true;
setTimeout(() => {
fn.apply(this, arguments);
// 重置status
status = false;
}, time);
}
}
}
const bindClick2 = throttle(clickButton, 1000);