每日一题:什么是防抖和节流?有什么区别?如何实现

1. 什么是防抖节流

  • 防抖就是无论如何触发事件,只在设定的n秒之后执行时间,如果在n秒内再次触发了事件,则重新计时
  • 节流就是在设定的时间内只触发一次

2. 区别

  • 防抖以最后一次触发为准,如果在设定的n秒内再次触发,则以新的事件时间为准
  • 节流以第一次触发为准,执行第一次时间后,在设定的n秒内触发都不会执行

3. 如何实现

  • 都利用了闭包
// 下面的代码共用此绑定事件
function doSomething() {
    console.log('input')
}

var input = document.getElementById('input')
// 防抖
input.oninput = throttle(doSomething, 5000, {
    leading: false
})

// 节流
input.oninput = throttle(doSomething, 5000, {
    leading: false
})

防抖

  • 利用定时器
  • 思路:
    1. 如果在设定的时间内触发,清除定时器重新计时
    2. 重新设定定时器,利用apply/call执行函数
    3. 可以增加立刻执行选项
  function debounce(func, wait, immediate) {
      var timeout, result;
      return function () {
          var context = this; // 解决event指向问题
          var args = arguments; // 传参
  
          if (timeout) clearTimeout(timeout);
          if (immediate) {
              // 如果已经执行过,不再执行
              var callNow = !timeout;
              timeout = setTimeout(function(){
                  timeout = null;
              }, wait)
              if (callNow) result = func.apply(context, args) // 只有立刻执行有返回值,使用settimeout执行函数,因为异步原因,result为undefined
          } else { // 非立刻执行保持原逻辑
              timeout = setTimeout(function(){
                  func.apply(context, args)
              }, wait);
          }
          return result;
      }
  }

节流

  • 利用定时器或时间戳
  • 总结:
    1. 时间戳方式:第一次进入立刻执行;定时器:并不立刻执行,在n秒之后执行
    2. 时间戳:停止触发了,就不执行了,因为是根据触发的时间去和上一次时间进行比较;定时器:停止触发了,仍然会执行
  • 思路:
    1. 结合两种方式的优点,既能立刻执行,停止触发也能执行最后一次
    2. 分两种判断逻辑:1. 时间戳 2. 定时器;定时器为空(第一次进入或定时器执行完成后)时进入时间戳逻辑;其余是进入定时器逻辑
function throttle(fn, wait, options) {
       var timeout = null, pre = 0;
        var context,arg;
        if (!options) options = {};


       var throttled = function () {
           context = this;
           arg = arguments;
           var now = +new Date();
           if (!pre && options.leading === false) pre = now; // +++ 禁止立刻执行,remaining > 0, 执行定时器逻辑
           var remaining = wait - (now - pre); // 计算还剩下多少时间执行下一次方法
           // 时间戳:立刻执行,第一次进入remaining必小于0,因为wait-now
           if (remaining <= 0) {
               console.log('时间戳')
               if (timeout) {
                   clearTimeout(timeout) // 第一次进入,清除定时器,为后面做准备
                   timeout = null
               }
               pre = now; // now时间赋值pre,频繁触发时,基本不会出现remaining <= 0,会走下面的逻辑
               fn.apply(context, arg)
               if (!timeout) context = arg = null; // +++ 不太明白,或许是手动清除内存?
           } else if (!timeout && options.trailing !== false) { // +++ 增加options.trailing !== false判断,停止触发不设置定时器了
               console.log('定时器')
               timeout = setTimeout(function () {
                   pre = options.leading === false ? 0 : +new Date(); // +++ 如果需要禁止立刻执行,需要重新将pre重置为0,避免进入时间戳逻辑
                   timeout = null;
                   fn.apply(context, arg)
                   if (!timeout) context = arg = null; // +++ 不太明白,或许是手动清除内存?
               }, remaining)
           }
       }

        throttled.cancel = function () {
            clearTimeout(timeout)
            pre = 0;
            timeout = null
        }
       return throttled;
}

参考链接

  1. 防抖
  2. 节流
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容