Abo

小抄:手把手带你写防抖与节流

性能优化:防抖与节流

面到麻木💀,害怕

        

防抖

防抖:反复执行,只会执行最后一次或者第一次。

实现:每次事件触发就删除原来的定时器,建立新的定时器。通过immediate选择是立即执行还是最后执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function debounce(fun, delay = 500,immediate = true) {
let timer = null //保存定时器
return function (args) {
let that = this
let _args = args
if (timer) clearTimeout(timer); //不管是否立即执行都需要首先清空定时器
if (immediate) {
if ( !timer) fun.apply(that, _args) //如果定时器不存在,则说明延时已过,可以立即执行函数
//不管上一个延时是否完成,都需要重置定时器
timer = setTimeout(function(){
timer = null; //到时间后,定时器自动设为null,不仅方便判断定时器状态还能避免内存泄露
}, delay)
}
else {
//如果是非立即执行版,则重新设定定时器,并将回调函数放入其中
timer = setTimeout(function(){
fun.call(that, _args)
}, delay);
}
}
}

节流

节流:一段时间内只能触发一次。

实现:用一个状态来控制执行,如果已经这段时间内已经执行过了,就不执行了。否则就执行。

定时器版

开始触发时等n秒后执行,停止触发后继续执行一次事件

1
2
3
4
5
6
7
8
9
10
11
const throttle = (fn, wait = 300) =>{
let timeId
return function(...args){
if(!timeId){
timeId = setTimeout(() =>{
fn.apply(this, ...args)
timeId = null
}, wait)
}
}
}

时间戳版

开始触发时立即执行,停止触发后不再执行事件

1
2
3
4
5
6
7
8
9
10
const throttle = (fn, wait = 300) =>{
let prev = 0
return function(...args){
let now = +new Date()
if(now - prev > wait){
prev = now
fn.apply(this, ...args)
}
}
}

这里补充一下面试过程想到的写法,同时间戳版

1
2
3
4
5
6
7
8
9
10
11
12
function throttle(func,wait) {
let preTime = Date.now();
let nowTime;
return function(...args) {
let that = this;
nowTime = Date.now();
if(nowTime - preTime >= wait) {
preTime = Date.now();
func.apply(that,args);
}
}
}

防抖节流合并-高级版节流

固定时间内会给用户一个响应

当 remain <= 0 时表示该执行了(保证了第一次触发事件就能立即执行和每隔 wait 时间执行一次)。

还没到时间的话就设定在 remain 时间后再触发(保证了最后一次还能再执行一次)。

在 remain 这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const throttle = (fn, wait = 300) => {
let prev = 0
let timeId
return function(...args){
timeId && clearTimeout(timeId)
let now = +new Date()
//remain:剩余时间
let remain = wait - (now - prev)
if(remain <= 0){
fn.apply(this, args)
prev = now
}
else{
timeId = setTimeout(fn, remain)
}
}
}