js 节流实战讲解

在《js 防抖实战讲解》中,我们了解了为什么要限制事件的频繁触发,以及如何做限制:
1. debounce 防抖
2. throttle 节流

今天讲讲节流的实现。

节流原理

如果你持续触发事件,每隔一段时间,只执行一次事件。
关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。

时间戳例子

当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 ),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。

根据描述,代码如下:

// 时间戳
  function throttle(func, wait) {
  	 var context, args;
  	 var previous = 0;  // 初始值为零

  	 return function() {
  	 	  var now = +new Date();
  	 	  context = this;
  	 	  args = arguments;
  	 	  if(now - previous > wait) {
  	 	  	 func.apply(context,args);
  	 	  	 previous = now;
  	 	  }
  	 }
  }
复制代码
throttle(getUserAction, 1000)
复制代码

效果演示如下:

我们可以看到:当鼠标点击按钮的时候,事件立即执行,最后一次点击如何没到规定的时间周期不执行,也就是说最后一次时间周期内没执行。

定时器例子

当触发事件的时候,我们设置个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,清空定时器,设置下个定时器

根据描述,代码如下:

// 定时器
  function throttle(func, wait) {
     var timeout,context;
     var previous;

     return function() {
         context = this;
         args = arguments;
         if(!timeout) {
         	  timeout = setTimeout(function() {
               timeout = null;
               func.apply(context, args)
         	  },wait)
         }
     }
  }
复制代码

效果演示如下:

我们可以看到,当点击按钮的时候,事件不会立即执行,1秒后执行了一次,此后每一秒执行一次,这里有一点要注意下,就是最后一次点击会在后面执行一次。

所以比较两个方法:

  1. 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
  2. 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件

两者结合

现在有个需求:我想要一个有头有尾的!就是点击按钮立刻执行,停止触发的时候还能再执行一次!
所以我们综合两者的优势,然后合起来写一版代码:

function throttle(func, wait) {
    var timeout, context, args, result;
    var previous = 0;

    var later = function() {
        previous = +new Date();
        timeout = null;
        func.apply(context, args)
    };

    var throttled = function() {
        var now = +new Date();
        //下次触发 func 剩余的时间
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
         // 如果没有剩余的时间了或者你改了系统时间
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}
复制代码

效果演示如下:

连续点击两次按钮,立即执行一次,一秒后又执行了一次,做到了有头也有尾。

优化

但是我有时也希望无头有尾,或者有头无尾,这个咋办?
那我们设置个 options 作为第三个参数,然后根据传的值判断到底哪种效果,我们约定:
leading:false 表示禁用第一次执行
trailing: false 表示禁用停止触发的回调
我们来改一下代码:

// 第四版
function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
        previous = options.leading === false ? 0 : new Date().getTime();
        timeout = null;
        func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function() {
        var now = new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}
复制代码

这里有一点需要注意:
那就是 leading:false 和 trailing: false 不能同时设置。

参考网址:
github.com/mqyqingfeng…

转载于:https://juejin.im/post/5cd279a2f265da038860ce62

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值