实现防抖、节流函数
在前端开发中会遇到一些频繁的事件触发,如
window的resize、scrollmousedown、mousemovekeyup、keydown我们不想让他频繁触发,如表单的提交,一定时间内我们只希望它提交一次而非多次。于是,就出现了防抖和节流。
# 防抖
# 防抖原理
你尽管触发事件,但在事件触发n秒后才执行;若一个事件触发后的n秒内又重新触发此事件,以新事件的时间为准,n秒后才执行。
# 注意点
- this 指向问题
this会指向window对象而非指向正确的对象 - event 对象问题
event对象获取为undefined - 立即执行 立即执行函数,然后等到停止触发n秒后,才可重新触发执行
- 返回值 函数可能有返回值,当不立即执行的时候,因使用了定时器,函数的返回值赋给变量,最后return时,值会一直是undefined,所以只在立即执行的时候返回函数的执行结果
- 取消 有时候,我们设置等待的时间有点长,可能需取消防抖。即给防抖函数加一个取消的功能
function debounce(func, wait, immediate) {
var timeout, result;
var debounced = function () {
// this 指向错误问题
var context = this;
// event 对象获取为undefined
var args = arguments;
if (timeout) clearTimeout(timeout);
// 立即执行
if (immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
// 返回值
if (callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
return result;
};
// 加取消功能
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
return debounced;
}
// 使用
var debounceFunc = debounce(func, 10000, true)
debounceFunc.cancel()
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
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
# 节流
# 节流原理及实现方案
原理:若你持续触发事件,每隔一段时间,只执行一次事件。 方案:时间戳、设置定时器
# 注意点
- 使用时间戳 当触发事件时,取当前时间戳,减去之前的时间戳(初始值为0),若 > 设置的时间周期,执行函数,更新时间戳为当前时间戳;若 < ,不执行。
- 使用定时器 当事件触发时,设一定时器,再次触发时,若定时器存在,就不执行;直到定时器执行完,执行函数,清空定时器,设置下个定时器
- 无头有尾或有头无尾
- 取消
function throttle(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function () {
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function () {
var now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
};
throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = null;
};
return throttled;
}
// leading:false 和 trailing: false 不能同时设置。
container.onmousemove = throttle(getUserAction, 1000);
container.onmousemove = throttle(getUserAction, 1000, {
leading: false
});
container.onmousemove = throttle(getUserAction, 1000, {
trailing: false
});
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
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
上次更新: 2022/09/02, 17:10:06