最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript 防抖与节流

    正文概述 掘金(Dear_Mr)   2021-03-08   571

    防抖与节流,它们的作用都是为了降低事件回调函数的调用频率,从而达到优化的目的。

    需要防抖与节流??

    我们一定需要防抖和节流吗??那么不使用防抖和节流会发生什么事情呢?

    可以举几个栗子:

    例子一:在Input输入框里,需要对用户的输入进行非法词汇判断?

    对该Input进行onchange事件的监听,在用户每次有输入字符的时候,都会去调用相关接口进行判断。所以就会出现下面的情况

    JavaScript 防抖与节流

    例子二:监听全局滚动事件,从而来实现比如,展示返回顶部、表格头部置顶,和其他改变某些元素的位置等等

    监听window.onscrol,它的事件触发频率在大多数情况下都是异常的高,在它的事件处理函数中会存在触发大量的回流重绘,像获取document.body.scrollTop的值,如果不增加相关限制,会造成页面卡顿,带来较差的用户体验

    通过这两个例子,可以看出我们要解决的问题—就是降低事件回调函数的执行频率。而所谓的防抖与节流,可以理解为,是在反复的尝试中,摸索出的一种有代表性的方案。

    代表方案——防抖

    这种方案可以实现的效果是:在某个时间段内如果连续触发事件,但是只有最后一次事件触发了回调函数。(注意这里不是停止触发就立即执行,而是有个delay的延迟) 该方案的关键在于判断,定时器是否存在,存在就清除

       function debounce(fn, delay) {
            let timer = null;
            return function () {
                let context = this;
                let args = [...arguments];
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    fn.apply(context, args)
                }, delay);
            }
       }
    

    在上面的第二个例子中,就可以这样使用

    let fn = debounce(controlFn, 1000); // controlFn 具体的事件处理函数
    window.addEventListener('scroll', fn);
    
    // 或者
    window.onscroll = debounce(controlFn, 1000);
    

    代表方案——节流

    效果:每隔一段时间就调用一次事件处理函数(在连续触发事件的过程中,只要到达规定的时间,就执行回调函数)

    使用时间戳

    function throttle(fn, delay) {
        let startTime = Date.now();
        return function () {
            let context = this;
            let args = [...arguments];
            let now = Date.now();
            if (now - startTime >= delay) {
                fn.apply(context, args);
                startTime = now;
            }
        }
    }
    

    使用定时器

    throttle(fn, delay) {
        let timer = null;
        return function () {
            let context = this;
            let args = [...arguments];
            if (!timer) {
                timer = setTimeout(() => {
                    fn.apply(context, args);
                    timer = null;
                }, delay);
            }
        }
    }
    

    源码

    understore.js有这么一段源码,我们一起来学习下~ 与我们前面写到的相比较,增加了immediate的参数,及相关判断是否要立即执行的逻辑

     // immediate 是否立即执行
      _.debounce = function(func, wait, immediate) {
        var timeout, result;
    
        var later = function(context, args) {
          timeout = null;
          if (args) result = func.apply(context, args);
        };
    
        var debounced = restArguments(function(args) {
          if (timeout) clearTimeout(timeout); // 防抖的处理
          if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(later, wait);
            if (callNow) result = func.apply(this, args);
          } else {
            timeout = _.delay(later, wait, this, args);
          }
    
          return result;
        });
        // 增加了取消定时器的操作
        debounced.cancel = function() {
          clearTimeout(timeout);
          timeout = null;
        };
    
        return debounced;
      };
    

    关于restArguments 函数,就相当于兼容完成ES6中的函数参数的结构赋值

      var restArguments = function(func, startIndex) {
        // func.length 代表 func函数中定义的形参的数量
        startIndex = startIndex == null ? func.length - 1 : +startIndex;
        return function() {
          // arguments.length - startIndex 就是 从startIndex 参数到 arguments.length 的参数数量,在es6中 (a, b, ...rest)
          var length = Math.max(arguments.length - startIndex, 0),
              rest = Array(length),
              index = 0;
          // 对rest数组进行赋值,值是从arguments的startIndex开始取的
          for (; index < length; index++) {
            rest[index] = arguments[index + startIndex];
          }
          // 对 function (...l), function(a, ...l), function  (a, b,...l) 这三种进行特殊处理
          switch (startIndex) {
            case 0: return func.call(this, rest);
            case 1: return func.call(this, arguments[0], rest);
            case 2: return func.call(this, arguments[0], arguments[1], rest);
          }
          // 将arguments从0到startIndex的内容,赋值到args中
          var args = Array(startIndex + 1);
          for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
          }
          // 对类似function  (a, b, c,...l)进行处理,args就类似于这样的形式[1, 2, 3, [4, 5, 6, 7]]
          args[startIndex] = rest;
          return func.apply(this, args);
        };
      };
    

    在看完这个restArguments 函数的实现,我有这样的疑问,为什么要在switch中单独处理,startIndex等于0、1、2这三种情况呢

    总结

    防抖和节流,需要我们按照自己的需求,去选择合适的指定方式。在上述代码的基础上,可按照你的实际需要,进行相应的改造和继续完善。


    起源地下载网 » JavaScript 防抖与节流

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元