函数防抖与节流

起因

因为在写前端页面时需要监控鼠标滚轮,这导致在很短时间内,会触发很多次函数,造成页面卡顿堵塞,正巧自己在看GitHub时看到了这个,所以自己记录一下。

防抖动

函数防抖就是让某个函数在上一次执行后,满足等待某个时间内不再触发此函数后再执行,而在这个等待时间内再次触发此函数,等待时间会重新计算。

underscore源码

_.debounce = function(func, wait, immediate) {
    // immediate默认为false
    var timeout, args, context, timestamp, result;
    var later = function() {
      // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func
      var last = _.now() - timestamp;
      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();
      // 第一次调用该方法时,且immediate为true,则调用func函数
      var callNow = immediate && !timeout;
      // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }
      return result;
    };
  };

简化版

  var debounce = function ( fn, wait, immdediate) {
    var timer = null,
        pre = 0;
    return function () {
      var context = this;
      var args = arguments;
      var now = +new Date();
      var left = now - pre - wait;
      pre = now;
      // 如果距离上一次函数调用已经超过了预计时间,或者此时没有设置timer
      if ( left > 0 ){
        fn.apply(context,args);
        return ;
      }
      //每次进入函数,都重新计算过期时间
      clearTimeout(timer);
      timer = setTimeout(function () {
        clearTimeout(timer);
        timer = null;
        fn.apply(context,args);
        pre = +new Date();  //调用了函数也更新pre
      },wait);
    }
  }

节流

每间隔某个时间去执行某函数,避免函数的过多执行,这个方式就叫函数节流。节流和防抖动最大的不同就是,节流保证一个时间段内至少会执行一次。可以想象成把水龙头拧小,它主要用于大量连续事件快速频繁触发的场景,比如:onscroll,onresize。

underscore源码

_.throttle = function(func, wait, options) {
    /* options的默认值
     *  表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
     *  options.leading = true;
     * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
     *  options.trailing = true; 
     * 注意:当options.trailing = false时,效果与上面的简单实现效果相同
     */
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 当到达wait指定的时间间隔,则调用func函数
      // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
      if (remaining <= 0 || remaining > wait) {
        // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // options.trailing=true时,延时执行func函数
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

简化版

var throttle = function ( fn, interval ) {
    var timer,
        first = true;
    return function () {
        var args = arguments,   // 重命名一下,防止迷糊
            _me = this;
        if ( first ) {      // 第一 次直接执行
            fn.apply(_me , args);
            return first = false;
        }
        if ( timer ){   // 如果上一次还没执行完,则直接返回
            return false;
        }
        timer = setTimeout(function () {  
            clearTimeout(timer);    // 时间结束后,先取消计时器,然后运行当前函数
            timer = null;
            fn.apply(_me, args);
        }, interval || 500);
    };
};

参考文章

https://github.com/hk029/front-end/blob/master/about_js/optimize.md

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JS函数防抖节流是两种不同的技术,用于解决类似的问题。 防抖(debounce)指在一定间内只执行一次函数,如果在这段间内再次触发函数,则重新计。这通常用于用户输入或窗口调整等场景,以防止不必要的计算或请求。 节流(throttle)指在一定间内只执行一次函数,如果在这段间内再次触发函数,则忽略。这通常用于限制事件的频率,例如鼠标滚动或网络请求。 总的来说,防抖节流都是为了避免因频繁的事件触发导致的性能问题。 ### 回答2: JS函数防抖节流是两种常用的优化手段,用于控制函数的执行频率,减少不必要的资源消耗。 函数防抖的原理是当事件触发后,不立即执行函数,而是等待一段间(如1000毫秒),如果在这段间内没有再次触发该事件,则执行函数;如果在该间段内再次触发了该事件,则重新计,等待一段间后再执行函数函数防抖常用于限制高频率触发的事件,比如输入框中的搜索功能,用户连续输入,只有在停止输入一段间后,才执行搜索操作。这样可以减少请求次数,避免不必要的资源浪费。 函数节流的原理是规定一个单位间,在这个单位间内,只能执行一次函数。如果在单位间内多次触发了该事件,只有第一次触发执行函数,其他触发被忽略。函数节流常用于限制高频率触发的事件,比如页面滚动的加载更多功能,用户快速滚动页面,只在固定的间间隔内触发一次加载更多。 总结来说,函数防抖是等待一段间后执行函数,期间事件还重新计,确保函数只在最后一次触发后执行;而函数节流是按照固定的间间隔执行函数,不管触发次数多少,只在规定的间间隔内执行一次函数函数防抖更适合控制高频率触发的事件,而函数节流更适合控制单位间内触发的次数。 ### 回答3: JS函数防抖节流都是用来控制函数执行频率的技巧,但它们的实现方式和应用场景有所不同。 函数防抖是指在事件触发后,等待一定间之后再执行函数。如果在这个等待间内再次触发了该事件,就重新计防抖的主要作用是减少函数执行的频率。常见的应用场景有输入框的关键词搜索,用户在输入过程中频繁触发输入事件,而我们希望用户停止输入后再进行搜索操作。使用函数防抖可以避免用户每输入一个字符都进行搜索,减少服务器负载。 函数节流是指在一定间间隔内只执行一次函数。相比于函数防抖函数节流更注重于函数执行的间隔间。如果在指定的间间隔内多次触发了该函数,只有在间隔间到达后才执行函数。常见的应用场景包括页面滚动事件、窗口大小改变事件等。通过函数节流,我们可以控制事件响应的频率,避免过于频繁的函数执行。 总结起来,函数防抖适用于限制函数执行频率,主要用于输入框搜索等场景。而函数节流适用于限制函数连续触发的频率,主要用于页面滚动、窗口大小改变等场景。两者的区别在于对间的处理方式,一个是等待一段间后执行,一个是一段间内只执行一次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值