Skip to main content

节流防抖

防抖 (debounce) 与节流 (throttle ) 在实际中会经常使用。这两天仔细看了一下,有点理解了。

防抖 Debounce

含义: 在规定的时间内, 一个事件不管触发多少次, 最终只会执行一次, 在这个时间内, 如果再次被触发, 则会重新计时。即每次触发事件有一个 delay,在这个 delay 时间内,不会再次触发事件,只有 delay 时间后,才会触发事件。 好记一点: 防抖就是防止手抖。比如一个输入框,本意来说输入框的 value 会根据实时输入变化,但是呢,有些人手有问题,在不停地抖抖抖,所以输入框的值就会变化得很快。如果这是一个搜索框,需要实时返回搜索数据,那么请求就会发送很多很多,会对资源造成浪费。

实现

举例说明:

<style>
.conatainer {
width: 100%;
height: 200px;
text-align: center;
line-height: 200px;
color: #fff;
background: #444444;
font-size: 30px;
}
</style>
<div class="container"></div>
<script>
const conainer = document.querySelector('.container')
let count = 1
function getAction(e) {
conainer.innerHTML = count + 1
}
</script>

不防抖

container.onmousemove=getAction

这样的话当我们移动鼠标,container 中的数字会不断变化,也就是说getAction 会触发很多次。

实现防抖

function debounce(fn, delay) {
let timer
return function () {
console.log(this)
clearTimeout(timer)
timer = setTimeout(fn, delay)
}
}
container.onmousemove = debounce(getAction, 1000)

现在移动鼠标到停止移动, getAction只会在停止后 1s 触发一次。如果要触发第二次只有再次移动鼠标然后停止等 1s。

防抖中的 this

现在防抖已经是成功了,但是还有一个问题:上面的console.log(this)按照我们的意愿应该打印 container对象,但是上面打印出了 window。所以还需要进行一下修改:

function debounce(fn, delay) {
let timer
return function () {
let that = this
clearTimeout(timer)
timer = setTimeout(function () {
console.log(that)
fn.apply(that)
}, delay)
}
}
container.onmousemove = debounce(getAction, 1000)

上面的代码暂存了一个this, 然后传入fnapply 方法, 这样在调用时this 的指向就会变成container 对象。

关于apply 请参阅Function.prototype.apply()

防抖函数的参数(event 对象)

js 在处理事件时, 会传入 evnet 对象;如果使用debounce函数,会打印 undefined:

function getAction(e) {
console.log(e)
conainer.innerHTML = count + 1
}
container.onmousemove = getAction //打印 event 对象
container.onmousemove = debounce(getAcion, 1000) // 打印undefined

所以,要对防抖函数进行修改,可以传入参数:

function debounce(fn, delay) {
let timer
return function () {
let that = this
let args = arguments
clearTimeout(timer)
timer = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}

arguments是函数参数的类数组对象,详情请参阅Arguments 对象

节流 Throttle

含义: 在一个时间段内, 一个事件只会触发一次,如果在这个时间段内,事件被触发多次,只有第一次会执行,其余的会被忽略。

实现

举例说明:

<style>
.conatainer {
width: 100%;
height: 200px;
text-align: center;
line-height: 200px;
color: #fff;
background: #444444;
font-size: 30px;
}
</style>
<div class="container"></div>
<script>
const conainer = document.querySelector('.container')
let count = 1
function getAction(e) {
conainer.innerHTML = count + 1
}
</script>

不节流

container.onmousemove = getAction

同上,当我们移动鼠标,container 中的数字会不断变化,也就是说getAction 会触发很多次。

实现节流

function throttle(fn, delay) {
let timer
return function () {
let that = this
let args = arguments
if (timer) {
return
}
timer = setTimeout(function () {
fn.apply(that, args)
timer = null
}, delay)
}
}
container.onmousemove = throttle(getAction, 1000)

当我们一直移动鼠标,container 中的数字会一直变化,但是变化的频率是固定的,1s,也就是说getAction 会触发很多次,但是只有每隔 1s 才会执行一次。