一、为什么要在 JavaScript 中使用防抖与节流:

  • JavaScript 是事件驱动的,大量的操作会触发事件,加入到事件队列中处理。
  • 而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生;

二、防抖 (延迟生效最后一次执行)

1,定义和原理

定义:函数防抖(debounce),即如果短时间内大量触发同一事件,都会重置计时器,等到事件不触发了,再等待规定的事件,才会执行函数。
原理:设置一个定时器,设置在规定的时间后触发事件处理,每次触发事件都会重置计时器。

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
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

// 方案一:状态位
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let valid = true
return () => {
if (!valid) {
return false
}
// 执行函数+把状态位设置为无效
valid = false
setTimeout(() => {
fn()
valid = true
}, delay)
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

// 方案二:时间戳
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let start
return () => {
let now = Date.now()
if (!start) {
start = now
}

if (now - start >= delay) {
fn()
start = null
}
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

// 方案三:setTimeout标记
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let timer = null
return () => {
if (!timer) {
timer = setTimeout(() => {
fn()
timer = null
}, delay)
}
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

3,应用场景:

  • 输入框中频繁的输入内容,搜索或者提交信息;
  • 频繁的点击按钮,触发某个事件;
  • 监听浏览器滚动事件,完成某些特定操作;
  • 用户缩放浏览器的 resize 事件;

总之,密集的事件触发,我们只希望触发比较靠后发生的事件,就可以使用防抖函数;

三、节流 (稀释函数的执行频率)

1,定义和原理

定义:函数节流(throttle),每隔一段时间就执行一次。
原理:设置一个定时器,设置 0.5 秒后执行事件,如果时间到了,就会执行函数并重置定时器,等待下一个 0.5 秒后再执行。

2,用处

滚动栏的位置查询,就能设置每 0.5 秒执行一次函数。

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

// 方案一:状态位
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let valid = true
return () => {
if (!valid) {
return false
}
// 执行函数+把状态位设置为无效
valid = false
setTimeout(() => {
fn()
valid = true
}, delay)
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

// 方案二:时间戳
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let start
return () => {
let now = Date.now()
if (!start) {
start = now
}

if (now - start >= delay) {
fn()
start = null
}
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

// 方案三:setTimeout标记
const throttle = (fn, delay) => {
/**
* @param [Function] fn 需要使用防抖的函数
* @param [Number] delay 毫秒,防抖期限值
*/
let timer = null
return () => {
if (!timer) {
timer = setTimeout(() => {
fn()
timer = null
}, delay)
}
}
}

const showTop = () => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('当前位置:' + scrollTop)
}
window.onscroll = throttle(showTop, 1000)

3,应用场景:

  • 监听页面的滚动事件;
  • 鼠标移动事件;
  • 用户频繁点击按钮操作;
  • 游戏中的一些设计;

总之,依然是密集的事件触发,但是这次密集事件触发的过程,不会等待最后一次才进行函数调用,而是会按照一定的频率进行调用;

四、相同不同:

相同:为了缓解函数频繁调用、大量计算导致页面卡顿
不同:防抖是将多次执行变为最后一次执行,节流是将多次执行变为在规定时间内只执行一次.