佳宸学习和分享笔记的地方

0%

防抖节流手写

防抖节流

这两个应用场景很多,自己手写一下,可以解决服务器压力

防抖

任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。

场景:用户输入停止一段时间过后再去获取数据

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>防抖</title>
</head>

<body>
<button id="debounce">防抖按钮</button>

<script>
let myDebounce = document.getElementById('debounce')
myDebounce.addEventListener('click', debounce(sayDebounce, 1000))

/**
* 防抖功能函数
* fn 要防抖的功能函数
* delay 延迟多久,毫秒
**/
function debounce (fn, delay, immediate) {
// 通过闭包,创建一个标记用来存放定时器的返回值
let timeout = null
// 触发事件回调时执行这个 teturn function
return function () {

// 每次当用户点击/输入的时候,把前一个定时器清除
if(timeout) clearTimeout(timeout)

// immediate true 首次触发执行
if(immediate) {
immediate = false
fn.apply(this, arguments)
}

// 创建一个新的 setTimeout,
// 这样就能保证点击按钮后的间隔内,如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
// 以防需要防抖的事件this指向被改变 绑定一下this指向
// 需要防抖的事件 形参 用argument 绑进来
// 定时器结束后传入 fn函数
fn.apply(this, arguments)
}, delay)
}
}

// 需要防抖的事件
function sayDebounce () {
console.log('防抖成功!')
}
</script>
</body>
</html>

节流

减少一段时间内触发的频率。

  • 方法一:设置flag 执行前flag flase 在定时器内节流时间执行后 设回 true
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>节流</title>
</head>

<body>
<button id="throttle">点击节流</button>

<script>
let myThrottle = document.getElementById('throttle')
myThrottle.addEventListener('click', throttle(sayThrottle, 1000))

// 节流函数体
function throttle (fn, delay) {
// 通过闭包保存一个标记
let flag = true
return function () {
// flag为false 说明还在节流时间中 退出函数
if (!flag) {
return
}
// 将 flag 设置为 false,防止执行之前再被执行
flag = false
// 定时器
setTimeout(() => {
fn.apply(this, arguments)
// 执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
flag = true
}, delay)
}
}

// 需要节流的事件
function sayThrottle () {
console.log('节流成功!')
}
</script>
</body>
</html>
  • 方法二:时间戳来判断是否已到执行时间。与上次函数执行的时间比较,大于节流时间就放行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// fn 是需要执行的函数
// wait 是时间间隔
const throttle = (fn, wait) => {
// 使用闭包保存,上一次执行 fn 的时间
let previous = 0
// 将 throttle 处理结果当作函数返回
return function () {
// 获取当前时间,转换成时间戳,单位毫秒
let now = +new Date()
// 将当前时间和上一次执行函数的时间进行对比
// 大于等待时间就把 previous 设置为当前时间并执行函数 fn
if (now - previous > wait) {
previous = now
fn.apply(this, arguments)
}
}
}

防抖加节流

节流里面包防抖

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
// fn 是需要节流处理的函数
// wait 是时间间隔
function throttle(fn, wait) {

// previous 是上一次执行 fn 的时间
// timer 是定时器
let previous = 0, timer = null

// 将 throttle 处理结果当作函数返回
return function (...args) {

// 获取当前时间,转换成时间戳,单位毫秒
let now = +new Date()

// ------ 新增部分 start ------
// 判断上次触发的时间和本次触发的时间差是否小于时间间隔
if (now - previous < wait) {
// 如果小于,则为本次触发操作设立一个新的定时器
// 定时器时间结束后执行函数 fn
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
previous = now
fn.apply(this, args)
}, wait)
// ------ 新增部分 end ------

} else {
// 第一次执行
// 或者时间间隔超出了设定的时间间隔,执行函数 fn
previous = now
fn.apply(this, args)
}
}
}

// DEMO
// 执行 throttle 函数返回新函数
const betterFn = throttle(() => console.log('fn 节流执行了'), 1000)
// 第一次触发 scroll 执行一次 fn,每隔 1 秒后执行一次函数 fn,停止滑动 1 秒后再执行函数 fn
document.addEventListener('scroll', betterFn)