前端开发之 节流与防抖

防抖节流的作用是什么?

节流 (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. 最后需要 还原原函数的参数。
相关推荐
俊俊谢37 分钟前
Admin.NET框架前端由于keep-alive设置缓存导致的onUnmount未触发问题
前端·缓存·.net
老魏爱学习1 小时前
记一次JS逆向-新榜数据nonce和xyz参数分析
开发语言·javascript·笔记·网络安全·php
Rverdoser1 小时前
Day10_CSS过度动画
前端·css
Jornici2 小时前
搭建vue-electron项目
前端·vue.js·electron
一雨方知深秋3 小时前
prop校验,prop和data区别
前端·javascript·webpack·data·prop校验
前端熊猫3 小时前
vue的生命周期和nextTick的关系
前端·javascript·vue.js
温柔的男孩像海洋丶3 小时前
LVGL学习之样式和时间,基于正点原子
前端·javascript·学习
zgscwxd8 小时前
thinkphp6模板调用URL方法生成的链接异常
前端·javascript·html
建群新人小猿8 小时前
退款成功订阅消息点击后提示订单不存在
java·开发语言·前端