防抖和节流都是为了解决__短时间内大量触发某函数__而导致的性能问题。比如触发频率过高导致响应速度跟不上触发频率,页面出现延迟、假死或卡顿的现象。

__防抖__:在事件被触发n秒后再执行回调函数,如果在这n秒内事件被重新触发,则重新计时。

常见应用场景:输入框的keyup事件,window的resize、scroll事件等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 防抖
const debounce = function(fn, delay){
let timer = null; // 用来保存计时器
return function(args){
const that = this;
clearTimeout(timer);

timer = setTimeout(function(){
fn.call(that, args); // 指定延迟后执行对应的回调函数
}, delay)
}

}
// 如果debounce中传入的是箭头函数的话,会导致this一直指向window
// 因为箭头函数函数体内的this对象就是定义函数时所处的对象。
inputNode.onkeyup = debounce(function(e){
console.log(e.target.value)
}, 500)

__节流__:在规定时间内,指定的事件回调只能被触发一次。如果在规定时间内,事件被多次触发,那么只有一次会生效。

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
29
30
31
32
33
34
35
36
37
38
39
40
// 因为在某个时间段只能执行一次,我们需要保存上一次执行的时间点和定时器
function throttle(fn, threshhold){
let timer = null;
let start = new Date();
return function(args){
let that = this;
let curr = new Date();

clearTimeout(timeout);

if(curr - start >= threshhold){
fn.call(that, args);
start = curr;
} else {
// 如果操作的时间很短,没有达到节流的阈值,那么在指定时间后自动执行一次。
timer = setTimeout(function(){
fn.call(that, args);
}, threshhold)
}
}
}

// 以上是使用时间戳实现的节流,下面看一下使用定时器如何实现
function throttle(fn, threshhold){
let timer = null;
return function(args){
let that = this;
if(!timer){
timer = setTimeout(function(){
fn.call(that, args);
clearTimeout(timer);
timer = null;
}, delay)
}
}
}

window.onmousemove = throttle(function(e){
console.log(e.pageX, e.pageY);
}, 200)