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

写在前面

文末有我在前端面试多年的经验文章!!!

防抖节流的作用是什么?

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

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

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

防抖函数应用场景:

就比如说这段代码:

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

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

防抖函数的核心:

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

那么首先我们要做的,就是创建一个防抖函数,这个函数的功能是设置一个定时器,每次点击都会触发一个定时器输出,但如果两次点击的间隔小于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外部的引用,于是由此形成了闭包。

最后:

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

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

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

接下来给大家推荐一篇我在前端面试多年的经验文章,希望大家看完以后都可以领取到心仪的offer哦!

文章:《聊聊前端面试那些事儿》

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值