面试工作常备防抖节流

一、防抖

没有使用防抖

没有使用防抖

使用了防抖

使用了防抖

立即执行:

一步一步实现防抖函数debounce()

调用防抖:

1
2
3
4
5
6
7
let count = 0
let container = document.querySelector('.container')
const doSomeThing = (e) => {
container.innerHTML = count++
}

container.onmousemove = debounce(doSomeThing, 300, true)

第一步,实现最最简单的防抖

1
2
3
4
5
6
7
const debounce = (func, waitTime) => {
let timeout;
return function () {
if(timeout) clearTimeout(timeout)
timeout = setTimeout(func, waitTime)
}
}

第二步,解决this的指向问题,改变执行函数doSomeThing()内部的指向

1
2
3
4
5
6
7
8
9
10
const debounce = (func, waitTime) => {
let timeout;
return function () {
const that = this
if(timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(that)
}, waitTime)
}
}

第三步,event指向问题

1
2
3
4
5
6
7
8
9
10
11
const debounce = (func, waitTime) => {
let timeout;
return function () {
let that = this
let args = arguments
if(timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(that, args)
}, waitTime)
}
}

第四步,加入立即执行参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const debounce = (func, waitTime, immediate) => {
let timeout;
return function () {
let that = this
let args = arguments
if(timeout) clearTimeout(timeout)
if (immediate) {
let callNow = !timeout //1.最开始timeout = undefined, callNow = true 4.timeout不为null,callNow = false
console.log(timeout)
timeout = setTimeout(() => { //3.鼠标在移动,timeout一直不执行,timeout一直不为null
timeout = null
}, waitTime)
if (callNow) func.apply(that, args) //2.callNow为true,鼠标一移动马上执行一次 5.就在waitTime时间内不再执行
} else {
timeout = setTimeout(() => {
func.apply(that, args)
}, waitTime)
}
}
}

二、节流

一步一步实现节流函数throttle()

调用防抖:

1
2
3
4
5
6
7
8
9
let count = 0
let container = document.querySelector('.container')
const doSomeThing = (e) => {
container.innerHTML = count++
}
container.onmousemove = throttle(doSomeThing, 1000, {
leading: true,
trailing: true
})

第一步,使用时间戳,顾头不顾尾,第一次触发,最后一次不会被触发

1
2
3
4
5
6
7
8
9
10
11
12
13
const throttle = (func, waitTime) => {
let oldTime = 0
return function () {
let that = this
let args = arguments
let nowTime = new Date().valueOf()
if( nowTime-oldTime > waitTime) {
console.log(`1`)
func.apply(that, args)
oldTime = nowTime
}
}
}

第二步,顾尾不顾头,第一次不会触发,最后一次会触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const throttle = (func, waitTime) => {
let timeout
return function () {
let that = this
let args = arguments
if (!timeout) {
console.log(`2`)
timeout = setTimeout(() => {
timeout = null
func.apply(that, args)
}, waitTime)
}
}
}

第三步,顾头顾尾

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
const throttle = (func, waitTime) => {
let timeout
let oldTime = 0
return function () {
let that = this
let args = arguments
let nowTime = new Date().valueOf()
if (nowTime - oldTime > waitTime) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
console.log("1")
func.apply(that, args)
oldTime = nowTime
} else if (!timeout) {
console.log("2")
timeout = setTimeout(() => {
oldTime = new Date().valueOf()
timeout = null
console.log("我是到最后一次才会执行到")
func.apply(that, args)
}, waitTime)
}
}
}

第四步,加入参数配置

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
const throttle = (func, waitTime, options) => {
console.log(options)
let timeout
let oldTime = 0
return function () {
let that = this
let args = arguments
let nowTime = new Date().valueOf()
if (options.leading === false) oldTime = nowTime
if (nowTime - oldTime > waitTime) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
console.log("1")
func.apply(that, args)
oldTime = nowTime
} else if (!timeout && options.trailing === true) {
console.log("2")
timeout = setTimeout(() => {
oldTime = new Date().valueOf()
timeout = null
console.log("我是到最后一次才会执行到")
func.apply(that, args)
}, waitTime)
}
}
}