前端开发之 节流与防抖

防抖节流的作用是什么?

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

其实很多前端框架都自带节流防抖功能,比如uniapp 好多框架都自带,当然没有自己也可以写,配合后端的限流 \ 熔断 \ 缓存 ,可以让服务器减轻不少压力.

防抖:是指在一定时间内,在动作被连续频繁触发的情况下,动作只会被执行一次,也就是说当调用动作过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外部的引用,于是由此形成了闭包。

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

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

总结:

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

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

  1. 其中debounce返回一个函数体,跟debounce形成了一个闭包。
  2. 子函数体中每次先销毁上一个setTimeout,再创建一个新的setTimeout
  3. 最后需要 还原原函数的 this 指向。
  4. 最后需要 还原原函数的参数。
相关推荐
三巧9 分钟前
纯CSS吃豆人(JS仅控制进度)
javascript·css·html
SummerGao.10 分钟前
【解决】layui layer的提示框,弹出框一闪而过的问题
前端·layui
软件技术NINI26 分钟前
html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码
javascript·css·html
天天扭码38 分钟前
从数组到对象:JavaScript 遍历语法全解析(ES5 到 ES6 + 超详细指南)
前端·javascript·面试
拉不动的猪40 分钟前
前端开发中常见的数据结构优化问题
前端·javascript·面试
街尾杂货店&40 分钟前
css word
前端·css
Мартин.43 分钟前
[Meachines] [Hard] CrimeStoppers LFI+ZIP-Shell+Firefox-Dec+DLINK+rootme-0.5
前端·firefox
冰镇生鲜43 分钟前
快速静态界面 MDC规则约束 示范
前端
技术与健康1 小时前
【解读】Chrome 浏览器实验性功能全景
前端·chrome
Bald Monkey1 小时前
【Element Plus】解决移动设备使用 el-menu 和 el-sub-menu 时,子菜单需要点击两次才会隐藏的问题
前端·elementui·vue·element plus