防抖与节流

实现防抖(debounce)与节流(throttle)

防抖(debounce)

在某个操作停止一段时间后才执行相应的处理函数。

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次。如果在设定的时间到来之前,又一次触发了事件,就重新开始延时。

应用场景

输入搜索;

首次不执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function debounce(fn, wait) {
let timer;

return function (...arg) {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(() => {
// fn.apply(this, arguments) 修正this指向
fn.apply(this, arg);
}, wait);
};
}

function debounce1(fn, wait) {
let timer;

return function (...arg) {
let context = this;

if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function () {
fn.apply(context, arg);
}, wait);
};
}

debounce返回了一个闭包,这个闭包会被连续频繁地调用,但是在闭包的内部限制了函数fn的执行,强制fn只在连续操作停止后执行一次。

fn.apply(this, arg)而不直接用fn()是为了修正this指向。不使用debounce时,在fn中打印this,本例中指向的是<input id="input"/>;加入debounce后,指向的是Window对象,所以要手动改正this指向。

React Hook useDebounce(首次不执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function useDebouce(fn, wait) {
const timerRef = useRef(0);
return function (...arg) {
if (timerRef.current) {
window.clearTimeout(timerRef.current);
}

timerRef.current = window.setTimeout(() => {
fn(...arg);
}, wait);
};
}

export default useDebounce

节流(throttle)

既要保证用户的行为立即有所反馈,又不要事件被过于频繁触发。

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

应用场景

页面上元素的拖拽;滑动加载更多;

时间戳实现throttle(首次不执行)

1
2
3
4
5
6
7
8
9
10
11
function throttle(fn, wait) {
let prev = Date.now();

return function (...arg) {
let now = Date.now();
if (now - prev >= wait) {
fn(...arg);
prev = Date.now();
}
};
}

定时器实现throttle(首次不执行)

1
2
3
4
5
6
7
8
9
10
11
12
function throttle(fn, wait) {
let timer;

return function (...arg) {
if (!timer) {
timer = window.setTimeout(() => {
fn(...arg);
timer = null;
}, wait);
}
};
}

react hook useThrottle(首次不执行)

1
2
3
4
5
6
7
8
9
10
11
function useThrottle(fn, wait) {
const timerRef = useRef();
return function (...arg) {
if (!timerRef.current) {
timerRef.current = window.setTimeout(() => {
fn(...arg);
timerRef.current = null;
}, wait);
}
};
}