一对好兄弟—防抖和节流

前言

防抖和节流对大家来说应该都不陌生,不仅是在面试中还是在实际开发中,经常会遇到防抖和节流的问题。对于很多初学的小伙伴,可能对于这两者的概念的都没有区分清楚,更不知道在哪些应用场景下用防抖还是节流。所以,本文主要带着大家来区分这两者的区别、具体的应用场景以及手写这两个函数。

防抖

场景

我们都遇到过这样的场景,在某个搜索框中输入自己想要搜索的内容

比如想要输入一个 javascript,

当输入第一字符j时,为了更好的用户体验,通常会出现对应的联想内容,这些联想内容通常是保存在服务器的 一次网络请求;

当输入ja时,再次发送网络请求,那么输入javascript一共需要10次网络请求,大大损耗我们整个系统的性能,无论是前端的事件处理,还是对于服务器的压力;

那么,我们只需要在合适的情况下发送网络请求。比如用户快速的输入了javascript,那么只发送一次网络请求,或者如果用户是输入一个j想了一会儿,这个时候j确实应该发送一次网络请求;

也就是我们应该监听用户在某个时间,比如500ms内,没有再次触发时间时,再发送网络请求;

微信截图_20211118195926.png

定义

防抖的定义:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数

当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;

当事件密集触发时,函数的触发会被频繁的推迟;

只有等待了一段时间也没有事件触发,才会真正的执行响应函数;

微信截图_20211118193140.png

节流

场景

很多人都玩过飞机大战的游戏,在飞机大战的游戏中,我们按下空格会发射一个子弹:

很多飞机大战的游戏中会有这样的设定,即使按下的频率非常快,子弹也会保持一定的频率来发射;

比如1秒钟只能发射一次,即使用户在这1秒钟按下了10次,子弹会保持发射一颗的频率来发射;

但是事件是触发了10次的,响应的函数只触发了一次;

微信截图_20211118200241.png

定义

节流的定义:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

当事件触发时,会执行这个事件的响应函数;

如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;

不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;

微信截图_20211118200624.png

实现

1.直接使用第三方工具库: underscore/lodash;

underscore地址:underscorejs.org/

微信截图_20211118222739.png

2.手写防抖函数

2.1 基本的防抖函数

index.html

    <script src="./debounce.js"></script>
    
    <input type="text">
    <script>
       const input = document.querySelector("input")
       let couter = 0;
       
       const inputChange = function() {
          console.log(`发送了第${++couter}次请求`);
       }

       input.oninput = debounce(inputChange, 2000)
    </script>
复制代码

debounce.js

function debounce(fn, delay) {
    //1.定义一个定时器变量,来保存上次的定时器
    let timer = null 
    // 2.真正执行的函数
    const _debounce = function () {
      // 3.如果定时器存在,取消上一次的定时器
      if(timer) clearTimeout(timer)
      timer = setTimeout(() => {
        // 外部传入的的函数
        fn()
      },delay)    
    }
    return _debounce
}
复制代码

2.2 this和...agrs

1.这里不能直接写的fn(),因为此时的this是指向window的,所以必须通过fn.apply()来改变this的指向,才能是this指向input对象。

2.如果有参数,利用...agrs来接收。

  function debounce(fn, delay) {
    let timer = null 
    const _debounce = function (...args) {   // ...args用来接收参数
      if(timer) clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this, args)    // 利用apply改变this的指向input
      },delay)     
    }
    return _debounce
}
复制代码

2.3 立即执行

定义: 在第一次输入的时候,先立即发送一次网络请求,后面的输入就是正常的防抖。

function debounce(fn, delay, immediate = false) {
    let timer = null 
    let isInvoke = false     // 设置一个新的自由变量来控制
    const _debounce = function (...args) {   
      if(timer) clearTimeout(timer)
      // 判断是否立即执行
      if(immediate && !isInvoke) {
        fn.apply(this, args) 
        isInvoke = true
      } else {
        timer = setTimeout(() => {
        fn.apply(this, args)
        isInvoke = false 
      },delay)     
      }  
    }
   return _debounce
}
复制代码

2.4:取消功能

定义: 当用户输入输入到最后时(请求还没发送),点击了取消按钮(相当于取消了发送)

    const debounceChange = debounce(inputChange, 3000)
    input.oninput = debounceChange
    
    // 取消功能
    const cancelBtn = document.quserySelector('#cancel')
    cancelBtn.onclick = function() {
     debounceChange.cancel()
    }
 

    function debounce(fn, delay) {       
        let timer = null      
        const _debounce = function () {   
         if(timer) clearTimeout(timer)
         timer = setTimeout(() => {     
         fn()
       },delay)   
        
        // 封装取消功能
        _debounce.cancle = function(){
          if(timer) clearTimeout(timer)   // 清除最后的定时器
        }
        
      }
    _
    return _debounce
}
复制代码

3.手写节流函数

节流比防抖稍微复杂一些,首要我们来理解一下节流的逻辑过程:

Snipaste_2021-11-19_19-27-40.png

3.1 基本的节流函数

   function throttle(fn, interval) {
   let lastTime = 0
   const _throttle = function() {
    const nowTime = new Date().getTime();
    const remainTime = interval - (nowTime - lastTime);
    // 执行函数
    if(remainTime <= 0) {
      fn();
      lastTime = nowTime
    }    
  }
  return _throttle
}
复制代码

应用场景

防抖

1.输入框中频繁的输入内容,搜索或者提交信息;

2.频繁的点击按钮,触发某个事件;

3.监听浏览器滚动事件,完成某些特

4.用户缩放浏览器的resize事件;

节流

1.监听页面的滚动事件

2.鼠标移动事件

3.用户频繁点击按钮操作

4.游戏中的一些设计

猜你喜欢

转载自juejin.im/post/7032248458170138631
今日推荐