函数节流与函数防抖

函数防抖(debounce)

应用场景

在浏览器 DOM 事件里面,有一些事件会随着用户的操作不间断触发。比如:重新调整浏览器窗口大小(resize),浏览器页面滚动(scroll),鼠标移动(mousemove)。也就是说用户在触发这些浏览器操作的时候,如果脚本里面绑定了对应的事件处理方法,这个方法就不停的触发。

这并不是我们想要的,因为有的时候如果事件处理方法比较庞大,DOM 操作比如复杂,还不断的触发此类事件就会造成性能上的损失,导致用户体验下降(UI 反映慢、浏览器卡死等)。所以通常来讲我们会给相应事件添加延迟执行的逻辑。

一段时间内多次触发同一事件,只执行最后一次或者只执行第一次,中间的不执行

代码

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>函数防抖</title>
</head>

<body>
<div id="demo" style="height: 5000px"></div>
<script>
var COUNT = 0;
var demo = document.getElementById('demo');

function testFn() {
demo.innerHTML += 'testFn 被调用了 ' + ++COUNT + '次<br>';
}

// version0: 《JavaScript高级程序设计》中的方法,把定时器ID存为函数的一个属性
/*
function throttle(method, context) {
clearTimeout(method.tid);
method.tid = setTimeout(function () {
method.call(context);
}, 100);
}

window.onscroll = function () {
throttle(testFn);
}
*/

// version1: -> 错误 timer不是相对全局的变量每次scroll会生成一个timer
/*
window.onscroll = function () {
var timer = null;
clearTimeout(timer);

timer = setTimeout(function () {
testFn();
}, 100);
};
*/

// version2: -> 正确, 但是会多添加一个相对全局的变量,有可能影响业务逻辑
/*
var timer = null;
window.onscroll = function () {
clearTimeout(timer);
timer = setTimeout(function() {
testFn();
}, 100);
};
*/

// version3: -> 正确,使用闭包
/**
* 函数节流方法
* @param Function fn 延时调用函数
* @param Number delay 延迟多长时间
* @return Function 延迟执行的方法
*/

/*
var throttle = function (fn, delay) {
var timer = null;

return function () {
clearTimeout(timer);
timer = setTimeout(function () {
fn();
}, delay);
}
};
*/

// 第一种调用方式
/*
var f = throttle(testFn, 200);
window.onscroll = function () {
f();
};
*/

// 第二种调用方式
/* window.onscroll = throttle(testFn, 200);*/
</script>
</body>

</html>

函数节流(throttle)

指连续触发事件但是在n秒中只执行一次函数。即2n秒内执行 2 次… 节流如字面意思,会稀释函数的执行频率

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
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>函数节流</title>
</head>

<body>
<div id="demo" style="height: 5000px"></div>
<script>
var COUNT = 0;
var demo = document.getElementById('demo');

function testFn() {
demo.innerHTML += 'testFn 被调用了 ' + ++COUNT + '次<br>';
}

// versin4:最终模式
var throttle = function (fn, delay, atleast) {
var timer = null;
var previous = null;

return function () {
var now = +new Date();

if (!previous) previous = now;

if (atleast && now - previous > atleast) {
fn();
// 重置上一次开始时间为本次结束时间
previous = now;
clearTimeout(timer);
} else {
clearTimeout(timer);
timer = setTimeout(function () {
fn();
previous = null;
}, delay);
}
}
};

// atleast参数选填
window.onscroll = throttle(testFn, 200, 1000);
// window.onscroll = throttle(testFn, 200);
</script>
</body>

</html>

完整代码