防抖与节流

82 阅读3分钟

防抖

概念

用户操作触发事件频繁,只要最后一次的操作

使用场景

<body>
  <input type="text">
  <script>
    let inp = document.querySelector("input")
    inp.oninput = function() {
      console.log(this.value);
    }
  </script>
</body>

当我们在输入框中输入一个“前”字时,以上代码在执行时,会出现如下图所示的情况 在这里插入图片描述 仅输入一个“前”字函数就执行了五次。那么当我们输入的字符串很长时,函数执行的次数也会变得很多;如果我们要发送请求的话,请求会变得非常频繁,导致性能下降;

如何把函数执行的次数降低,就是防抖的使用场景

第一种方式-非立即执行

当用户进行输入时,不会立即执行函数,而是等待 n 秒后再去执行函数,如果在此期间用户有新的输入,则重新计算 n;

    let inp = document.querySelector("input")
    let timer = null
    inp.oninput = function () {
      if (timer !== null) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        console.log(this.value);
      }, 500)
    }

第一种方式的优化

原理一样,只不过优化后的代码更容易维护,并且没有又创建一个全局变量

    inp.oninput = debouce(function(){
      console.log(this.value);
    },500)
    function debouce(fn, delayTime) {
       let timer = null
       return function () {
        if (timer !== null) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
           fn.call(this)
        }, delayTime)
      }
    } 

第二种方式-立即执行

用户第一次输入时立刻触发,然后n秒后不触发该事件才能继续执行函数

     function debouce(fn, delayTime) {
      let timer = null
      let flag = true
      return function () {
        clearTimeout(timer)
        if(flag) {
          fn.call(this)
          flag = false
        }
        timer = setTimeout(() => {
          flag = true
        }, delayTime)
      }
    } 

结合版本

function debounce_merge(fn,delayTime = 500,isImmediate = false){
  let timer= null;
  let flag = true;
  if(isImmediate){
    return function(){
      clearTimeout(timer);
      if(flag){
        fn.call(this);
        flag = false
        }
      timer = setTimeout(() => { flag = true},delayTime)
    }
  }
  return function(){
    clearTimeout(timerId);
    timer = setTimeout(() => {
      fn.call(this)
    },delayTime)
  }
}

节流

概念

用户触发事件频繁,减少事件执行的次数;如触发1000次,使用节流使其触发次数减少到10

使用场景

<body>
  <script>
    window.onscroll = ()=> {
      console.log("hello world!");
    }
    // 
  </script>
</body>

当我们滚动上图代码展示的页面时 会发生这样的效果 在这里插入图片描述 稍微滚动,函数就会被执行很多次;我们想让其执行次数减少到我们想让它执行的次数,这时候我们就要用到节流了

第一种方法 - 非立即执行

通过定时器限定多久执行一次

   let flag = true
      window.onscroll = ()=> {
        if(flag) {
          setTimeout(()=> {
            console.log("Hello world");
            flag = true
          },500)
        }
        flag = false
      }

函数优化

优化方法与防抖函数的优化相同

 window.onscroll = throttle(function(){
      console.log(this.value);
    },500)
    function throttle(fn, delayTime) {
      let flag = true
      return function () {
        if (flag) {
          setTimeout(() => {
            fn()
            flag = true
          }, delayTime)
        }
        flag = false
      }
    }

第二种方法 - 立即执行

    function throttle(fn, delayTime) {
      let flag = true
      return function () {
        if (flag) {
          setTimeout(() => {
            fn()
            flag = true
          }, delayTime)
        }
        flag = false
      }
    }

第三种方法 - 时间戳版本

创建时间戳,在时间戳内,函数只执行一次

    window.onscroll = throttle(function(){
      console.log("Hello world!");
    },1000)
    function throttle(fn, delayTime) {
      let pre = 0
      return function(){
        let now = Date.now()
        if(now-pre>delayTime) {
          fn.call(this)
          pre = now
        }
      }
    }

总结

  • 相同点
    • 防抖与节流都是为了防止函数在短时间内被频繁的触发
  • 不同点
    • 防抖函数只执行最后一次的操作,类似游戏中的回城,在回城期间,进行操作会打断回城

    • 节流类似将多个操作合并为一个,类似于游戏中的技能释放,释放后技能会进入CD,CD完成后才能再次进行施法