前端开发之 节流与防抖

防抖节流的作用是什么?

节流 (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. 最后需要 还原原函数的参数。
相关推荐
peakmain92 分钟前
Gradle 8.11.1的升级之旅
前端
一拳不是超人13 分钟前
PWA渐进式Web应用技术深度解析
前端·pwa
KaneLogger15 分钟前
视频转文字,别再反复拖进度条了
前端·javascript·人工智能
前端风云志28 分钟前
JavaScript中如何遍历对象?
javascript
zwjapple7 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
像风一样自由20209 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
aiprtem9 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊9 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术10 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing10 小时前
0704-0706上海,又聚上了
前端·新浪微博