- 参考文章 https://juejin.cn/post/6844903669389885453#heading-9
函数防抖(debounce)
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
<button>剁手按钮</button>
<script>
const button = document.querySelector('button')
const payMoney = function () {
console.log('剁手成功');
console.log(this);
}
function dobounce(fun, time) {
let timer
return function () {
const that = this
clearTimeout(timer)
timer = setTimeout(() => {
fun.call(that)
}, time);
}
}
button.addEventListener('click', dobounce(payMoney, 1000))
</script>
回调函数首次不自动执行
如果在button的监听事件中直接调用payMoney函数,则会发生还没有点击按钮函数就已经执行的情况,所以这里需要用到高阶函数,定义一个dobounce函数将payMoney函数return出来,这样就实现了不点击按钮函数就不执行的效果。
清除延时
要清除延时,我们就需要将整个setTimeout赋值给一个变量,这里我设置为timer,很显然,想要清除timer,就必须先定义timer,因此会有萌新写成这样:
function dobounce(fun, time) {
return function () {
let timer
const that = this
console.log('已点击');
clearTimeout(timer)
timer = setTimeout(() => {
fun.call(that)
}, time);
}
};
这样表面上达到了setTimeout执行之前清除延时的效果,但实际效果是,延时结束后,延时期间的所有点击都生效了, 这是因为每次点击执行函数之间都是独立的,每次点击都定义了一个新的timer,它们之间没有联系 。要解决这个问题其实也简单,我们把timer的定义提到外层,利用闭包的特性,这样每次点击执行的函数访问到的都是同一个timer
this指向
表面上来看,防抖函数到这里就已经实现了,看不出有啥问题,但是如果我们打印payMoney函数中的this,会发现它指向window而不是button,这就是this丢失了,虽然表面上看起来没问题,但在实际开发中却会产生令人头疼又难以发现的bug,所以我们需要绑定this。我在setTimeout之前定义了一个that将this保存了起来,在setTimeout中使用call(that)实现绑定this的效果。到这里防抖函数才算完全实现了。
案例分析
let biu = function () {
console.log('biu biu biu',new Date().Format('HH:mm:ss'))
}
let boom = function () {
console.log('boom boom boom',new Date().Format('HH:mm:ss'))
}
setInterval(debounce(biu,500),1000)
setInterval(debounce(boom,2000),1000)
这个示例就很好的解释了,如果在时间间隔内执行函数,会重新触发计时。biu会在第一次1.5s执行后,每隔1s执行一次,而boom一次也不会执行。因为它的时间间隔是2s,而执行时间是1s,所以每次都会重新触发计时
函数节流(throttle)
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
<input type="text" id="throttle">
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
let inputc = document.getElementById('throttle');
let throttleAjax = throttle(updateInp, 1000);
function updateInp(e){
console.log(e);
}
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
});
- 可以看到,我们在不断输入时,updateInp会按照我们设定的时间,每1s执行一次。
案例分析
```json let biubiu = function () { console.log(‘biu biu biu’, new Date()) };
setInterval(throttle(biubiu,1000),10) ```
不管我们时间设置的多小 ,总是1s内只执行一次。
总结
- 函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
- 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。