美文网首页
JS 节流与防抖

JS 节流与防抖

作者: 风之化身呀 | 来源:发表于2018-10-15 23:57 被阅读20次

本文的主角是节流 throttle 与防抖 debounce。

  • 区别
    节流强调的是m秒内函数最多触发一次,注意这里的用词最多,也就是可能不触发,一般第一次和最后一次可能不会触发;
    防抖强调的是函数两次调用的间隔必须大于m秒,如果函数触发的频率一直小于m秒,那么只有最后一次才会触发函数执行。

  • 场景
    节流:当给document加scroll事件时,假定处理函数为fn,那么当滑动鼠标时scroll事件会不断的被触发,影响滑动性能,这时可以用节流处理一下 throttle(fn,time),可以保证fn在time时间内只触发一次
    防抖:当做随着输入框输入不同内容展示不同的结果列表类似需求时,一般会绑定input的change事件,该事件在用户输入过程中会多次被触发,这时可以用防抖处理一下 debounce(fn,time),可以保证在用户输入完time时间后才触发fn。这里不用 throttle,因为debounce更加符合

  • 简易实现
    throttle:

function throttle(fn,time,imediate) {
    let timer = null;
    return function() {
        const args = arguments;
                const _this = this;
                const callNow = imediate && !timer
        if (timer) {
            return;
        }
        timer = setTimeout(()=>{
            timer = null;
        },time);
                if(callNow){
                        fn.apply(_this,args);
                }
    }
}

debounce:

function debounce(fn,time,imediate) {
    let timer = null;
    return function() {
        const args = arguments;
                const _this = this;
                const callNow = imediate && !timer
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(()=>{
            timer = null;
        },time)
                if(callNow){
                        fn.apply(_this,args);
                }
    }
}
  • underscore实现
/**
 * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
 *
 * @param  {function}   func      回调函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。
 *                                如果想忽略结尾函数的调用,传入{trailing: false}
 *                                两者不能共存,否则函数不能执行
 * @return {function}             返回客户调用函数   
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 之前的时间戳
    var previous = 0;
    // 如果 options 没传则设为空对象
    if (!options) options = {};
    // 定时器回调函数
    var later = function() {
      // 如果设置了 leading,就将 previous 设为 0
      // 用于下面函数的第一个 if 判断
      previous = options.leading === false ? 0 : _.now();
      // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // 获得当前时间戳
      var now = _.now();
      // 首次进入前者肯定为 true
      // 如果需要第一次不执行函数
      // 就将上次时间戳设为当前的
      // 这样在接下来计算 remaining 的值时会大于0
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 如果当前调用已经大于上次调用时间 + wait
      // 或者用户手动调了时间
      // 如果设置了 trailing,只会进入这个条件
      // 如果没有设置 leading,那么第一次会进入这个条件
      // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
      // 其实还是会进入的,因为定时器的延时
      // 并不是准确的时间,很可能你设置了2秒
      // 但是他需要2.2秒才触发,这时候就会进入这个条件
      if (remaining <= 0 || remaining > wait) {
        // 如果存在定时器就清理掉否则会调用二次回调
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // 判断是否设置了定时器和 trailing
        // 没有的话就开启一个定时器
        // 并且不能不能同时设置 leading 和 trailing
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };



/**
 * underscore 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,是否立即调用函数
 * @return {function}             返回客户调用函数
 */
_.debounce = function(func, wait, immediate) {
    var timeout, args, context, timestamp, result;

    var later = function() {
      // 现在和上一次时间戳比较
      var last = _.now() - timestamp;
      // 如果当前间隔时间少于设定时间且大于0就重新设置定时器
      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        // 否则的话就是时间到了执行回调函数
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };

    return function() {
      context = this;
      args = arguments;
      // 获得时间戳
      timestamp = _.now();
      // 如果定时器不存在且立即执行函数
      var callNow = immediate && !timeout;
      // 如果定时器不存在就创建一个
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        // 如果需要立即执行函数的话 通过 apply 执行
        result = func.apply(context, args);
        context = args = null;
      }

      return result;
    };
  };

相关文章

  • 函数节流与函数防抖

    函数节流与函数防抖的区别JS魔法堂:函数节流(throttle)与函数去抖(debounce)函数的防抖与节流 自...

  • 前端性能优化-浅谈js防抖和节流

    浅谈js防抖和节流

  • 防抖和节流

    一、防抖:只执行最后一次 js防抖方法 一、节流:控制执行次数 节流

  • JavaScript:函数防抖与节流

    什么是函数防抖与节流? 函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段。 定义: 防抖: n 秒后在...

  • 微信小程序防抖、节流的使用

    1、防抖、节流函数的封装 在utils文件夹下新建utils.js文件,然后写入节流和防抖函数 在.js页面引入并使用:

  • js函数的防抖(debounce)与节流(throttle)

    js 函数的防抖(debounce)与节流(throttle)[https://www.cnblogs.com/c...

  • js的防抖和节流

    js的 防抖(debounce)和 节流(throttle ) 防抖和节流一般用于高频触发事件,属于浏览器性能优化...

  • JS 节流与防抖

    本文的主角是节流 throttle 与防抖 debounce。 区别节流强调的是m秒内函数最多触发一次,注意这里的...

  • js防抖与节流

    在前端开发中会遇到一些频繁的事件触发,例如input,keyup,keydown,scroll,resize,mo...

  • js防抖与节流

    1.节流(throttle) 定义:先定义个执行周期,当时间执行时间大于等于执行周期,那就进入新的周期;如果小于执...

网友评论

      本文标题:JS 节流与防抖

      本文链接:https://www.haomeiwen.com/subject/uaaqzftx.html