防抖
Galloping_Leo 2020-09-29 JavaScript
# 防抖
const debounce = (func, wait) => {
let timerId
// 这个返回的函数就是 foo
return (...rest) => {
// args => ['123']
const args = rest
// const that = this
clearTimeout(timerId)
timerId = setTimeout(() => {
func(args)
// func.apply(that, args)
}, wait)
}
}
const foo = debounce((num) => {
console.log(num) // 123
}, 500)
foo('123')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# debounce 的实现过程
# 实现过程1
- 基本实现:
const debounce = (func, wait) => {
let timerId
return () => {
clearTimeout(timerId)
timerId = setTimeout(() => {
func()
}, wait)
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
// 测试:
let count = 0
const getCity = () => console.log('鼠标移动了', ++count)
// 鼠标移动事件,在鼠标停止移动后,再经过 500ms 才会执行
window.onmousemove = debounce(getCity, 500)
1
2
3
4
5
6
2
3
4
5
6
# 实现过程2
- 处理 this 指向:
const debounce = (func, wait) => {
let timerId
// 注意:此处不要使用箭头函数,箭头函数 this 指向的问题!!!
return function() {
const context = this
clearTimeout(timerId)
timerId = setTimeout(() => {
func.apply(context)
}, wait)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
// 测试
<div id="container" style="height: 100px; background-color: skyblue"></div>
// 注意:箭头函数中 this 指向问题
function getCity() {
console.log('鼠标移动了 this:', this)
}
const container = document.getElementById('container')
// 鼠标移动事件,在鼠标停止移动后,再经过 500ms 才会执行
container.addEventListener('mousemove', debounce(getCity, 500))
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 实现过程3
- 事件对象(处理参数):
const debounce = (func, wait) => {
let timerId
return function() {
const context = this
const args = arguments
clearTimeout(timerId)
timerId = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
// 测试:
function getCity(e) {
console.log('鼠标移动了,事件对象:', e)
}
const container = document.getElementById('container')
container.addEventListener('mousemove', debounce(getCity, 500))
1
2
3
4
5
6
7
2
3
4
5
6
7
# 实现过程4
- 立即执行:
const debounce = (func, wait, leading) => {
let timerId
return function() {
const context = this
const args = arguments
if (timerId) clearTimeout(timerId)
if (leading === true) {
if (!timerId) func.apply(context, args)
timerId = setTimeout(() => {
// 重置 timerId 的值。
timerId = null
}, wait)
} else {
timerId = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 测试:
container.addEventListener('mousemove', debounce(getCity, 500, true))
1
2
3
2
3
- 分解步骤1:
// 1 添加立即执行判断
const debounce = (func, wait, leading) => {
let timerId
return function() {
const context = this
const args = arguments
if (leading === true) {
clearTimeout(timerId)
timerId = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 分解步骤2:
// 2 立即调用
const debounce = (func, wait, leading) => {
let timerId
return function() {
const context = this
const args = arguments
if (leading === true) {
// 问题:因为直接调用了 func,没有了 debounce 的特性
func.apply(context, args)
clearTimeout(timerId)
timerId = setTimeout(() => { }, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 分解步骤3:
// 3 调用一次
const debounce = (func, wait, leading) => {
let timerId
let callNow = true
return function() {
const context = this
const args = arguments
if (leading === true) {
// 问题:只会触发一次
if (callNow) func.apply(context, args)
callNow = false
clearTimeout(timerId)
timerId = setTimeout(() => { }, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 分解步骤4:
// 4 实现debounce效果
const debounce = (func, wait, leading) => {
let timerId
let flag = true
return function() {
const context = this
const args = arguments
if (leading === true) {
if (flag) func.apply(context, args)
flag = false
clearTimeout(timerId)
timerId = setTimeout(() => {
// 在间隔时间达到后,重置标志。这样,再触发该事件时,上面的 if(flag) 判断就成了
flag = true
}, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 分解步骤5:
// 5 使用 timerId 代替 flag
const debounce = (func, wait, leading) => {
let timerId
return function() {
const context = this
const args = arguments
if (leading === true) {
if (!timerId) func.apply(context, args)
clearTimeout(timerId)
timerId = setTimeout(() => {
// 重置 timerId 的值。
timerId = null
}, wait)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 实现过程5
- 返回值:只能处理 leading=true 的情况
const debounce = (func, wait, leading) => {
let timerId, result
return function() {
const context = this
const args = arguments
if (timerId) clearTimeout(timerId)
if (leading === true) {
if (!timerId) result = func.apply(context, args)
timerId = setTimeout(() => {
// 重置 timerId 的值。
timerId = null
}, wait)
} else {
timerId = setTimeout(() => {
func.apply(context, args)
}, wait)
}
return result
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 测试
function getCity(e) {
console.log('鼠标移动了,事件对象:', e)
return 666
}
const container = document.getElementById('container')
const btnCancel = document.getElementById('container')
const handleMouseMove = debounce(getCity, 2000, true)
container.addEventListener('mousemove', handleMouseMove)
// 结果: 666
console.log('测试返回值:', handleMouseMove())
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 实现过程6
- 取消
const debounce = (func, wait, leading) => {
let timerId, result
function debounced() {
const context = this
const args = arguments
if (timerId) clearTimeout(timerId)
if (leading === true) {
if (!timerId) result = func.apply(context, args)
timerId = setTimeout(() => {
// 重置 timerId 的值。
timerId = null
}, wait)
} else {
timerId = setTimeout(() => {
func.apply(context, args)
}, wait)
}
return result
}
// 取消
debounced.cancel = function() {
clearTimeout(timerId)
timerId = null
}
return debounced
}
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
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
// 测试:
function getCity(e) {
console.log('鼠标移动了,事件对象:', e)
}
const container = document.getElementById('container')
const btnCancel = document.getElementById('container')
const handleMouseMove = debounce(getCity, 2000, true)
container.addEventListener('mousemove', handleMouseMove)
btnCancel.addEventListener('click', () => {
handleMouseMove.cancel()
})
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13