注册
web

面试必问,防抖函数的核心是什么?

防抖节流的作用是什么?


节流(throttle)与 防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。


防抖:是指在一定时间内,在动作被连续频繁触发的情况下,动作只会被执行一次,也就是说当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间,所以短时间内的连续动作永远只会触发一次,比如说用手指一直按住一个弹簧,它将不会弹起直到你松手为止。


节流:是指一定时间内执行的操作只执行一次,也就是说即预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期,一个比较形象的例子是人的眨眼睛,就是一定时间内眨一次。


防抖函数应用场景:


就比如说这段代码:


   let btn = document.getElementById('btn')
btn.addEventListener('click', function() {
console.log('提交'); // 换成ajax请求
})

当你点击按钮N下,它就会打印N次“提交”,但如果把 console 换成 ajax 请求,可想而知后端接受到触发频率如此之高的请求,造成的页面卡顿甚至瘫痪的后果。


1722263640946.png


防抖函数的核心:


面对此种情形,我们必须在原有的基础上作出改进,做到在规定的时间内没有下一次的触发,才执行的效果。


那么首先我们要做的,就是创建一个防抖函数,这个函数的功能是设置一个定时器,每次点击都会触发一个定时器输出,但如果两次点击的间隔小于1s,则销毁上一个定时器,达到最后只有一个定时器输出的效果。



定时器:


在防抖节流中,最为重要的一个部分就是定时器,就比如下面这段代码,setTimeout的功能就是设置一个定时器,让setTimeout内部的代码延迟执行在 1000 毫秒后。


  setTimeout(function(){
console.log('提交');
}, 1000)

特别需要注意一点的是,定时器中回调函数里的 this 指向会更改成指向 window。



于是我们创建专门的debounce函数用于实现防抖,把handle交给debounce处理,再在debounce内部设置一个setTimeout定时器,handle的执行推迟到点击事件发生的一秒后,这样一来,我们就实现了初步的想法。


  let btn = document.getElementById('btn')

function handle(){
console.log('提交', this); // 换成ajax请求
}

// 创建专门的debounce函数用于防抖,把handle交给debounce处理
btn.addEventListener('click', debounce(handle))

// 将点击事件推迟一秒
function debounce(fn){
return function() {
// 设置定时器
setTimeout(fn, 1000)
}

}

那么关键来了,我们又在原基础上添加一个timer用于接收定时器返回的值(通常称为定时器的ID),然后设置clearTimeout(timer)通过timer取消之前通过 setTimeout 创建的定时器。


通过这段代码,我们便实现了如果在 1s 内频繁点击的话,上一次点击的事件都会被下一次点击取消,从而达到规定的时间内没有下一次的触发,再执行的防抖目的!


  let btn = document.getElementById('btn')

function handle(){
console.log('提交', this); // 换成ajax请求
}

// 创建专门的debounce函数用于防抖,把handle交给debounce处理
btn.addEventListener('click', debounce(handle))

// 防抖函数
function debounce(fn){
let timer = null; // 接收定时器返回的ID
return function() {
// 设置定时器
clearTimeout(timer); // 取消之前通过 `setTimeout` 创建的定时器
timer = setTimeout(fn, 1000);
}

}

但是别忘了,我们之前提到过,定时器改变了handle中 this 指向,要做到尽善尽美,我们必须通过显示绑定修正 this 的指向。


同时别忘记还原原函数的参数。


利用箭头函数不承认 this 的特性,我们将代码修改成这样:


  let btn = document.getElementById('btn')

function handle(e){
console.log('提交'); // 换成ajax请求
}

// 创建专门的debounce函数用于防抖,把handle交给debounce处理
btn.addEventListener('click', debounce(handle))

// 防抖函数
function debounce(fn){
let timer = null; // 接收定时器返回的ID
return function(e) {
// 设置定时器
clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this,e); // 修正this的同时归还原函数的参数
}, 1000)
}

}

至此,大功告成!


防抖函数核心机制:


同时需要理解的是:防抖函数的核心机制就是闭包,当每一次点击会产生debounce执行上下文,随后debounce执行完其上下文又被反复销毁,但是其中的变量timer又始终保持着对function外部的引用,于是由此形成了闭包。


关于 this 的指向可以参考这篇文章:juejin.cn/post/739763…


关于闭包概念可以参考这篇文章:juejin.cn/post/739762…


最后:


那么现在我们可以总结出这个防抖函数的核心理念四大要点


核心理念:点击按钮后,做到在规定的时间内没有下一次的触发,才执行



  1. 其中debounce返回一个函数体,跟debounce形成了一个闭包。
  2. 子函数体中每次先销毁上一个setTimeout,再创建一个新的setTimeout
  3. 最后需要 还原原函数的 this 指向。
  4. 最后需要 还原原函数的参数。

作者:木笙
来源:juejin.cn/post/7400253623790272547

0 个评论

要回复文章请先登录注册