在日常开发中,我们经常会处理各种事件,比如常见的click、scroll、 resize等等。仔细一想,会发现像scroll、scroll、onchange这类事件会频繁触发,如果我们在回调中计算元素位置、做一些跟DOM相关的操作,引起浏览器回流和重绘,频繁触发回调,很可能会造成浏览器掉帧,甚至会使浏览器崩溃,影响用户体验。针对这种现象,目前有两种常用的解决方案:防抖和节流。

一、 防抖(debounce)

所谓防抖,就是指触发事件后,就是把触发非常频繁的事件合并成一次去执行。即在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算。

1. 非立即执行

1
2
3
4
5
6
7
8
9
10
export default function debounce(fn, wait) {
let timeout;
return function() {
let ctx = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
fn.apply(ctx, args);
}, wait);
};
}

2. 立即执行

1
2
3
4
5
6
7
8
9
10
11
12
export default function debounce(fn, wait) {
let timeout;
return function () {
let context = this , args = arguments;
clearTimeout(timeout);

timeout = setTimeout(() => {
timeout = null;
}, wait);
if (!timeout) fn.apply(context, args);
}
}

3. 防抖合并

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
/**
* @desc 函数防抖
* @param fn 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
export default function debounce(fn, wait, immediate) {
let timeout;
return function () {
let context = this , args = arguments;
clearTimeout(timeout);

if (immediate) {
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (!timeout) fn.apply(context, args);

} else {
timeout = setTimeout(() => {
fn.apply(context, args)
}, wait);
}
}
}

二、节流(throttle)

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。 节流会稀释函数的执行频率。当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到delay时间后,定时器执行执行函数,并且清空定时器,这样就可以设置下个定时器。

1. 时间戳

1
2
3
4
5
6
7
8
9
10
11
function throttle(fn, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this , args = arguments;
if (now - previous > wait) {
fn.apply(context, args);
previous = now;
}
}
}

2. 定时器

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

3. 节流合并

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
/**
* @desc 函数节流
* @param fn 函数
* @param wait 延迟执行毫秒数
* @param type 1 时间戳,0 定时器
*/
function throttle(fn, wait, type) {
let previous = 0 , timeout;
return function() {
let context = this;
let args = arguments;
if(type) {
let now = Date.now();
if (now - previous > wait) {
fn.apply(context, args);
previous = now;
}
}else {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
fn.apply(context, args)
}, wait)
}
}
}
}


参考文章: