写一个throttle函数

上一篇写了(写一个debounce函数),这篇就写一下throttle函数。

throttle是怎样的一个函数呢?和debounce函数又有什么区别呢?

throttle就是节流阀,控制在一定时间范围内只执行一次函数,其实根据节流阀的名字也可以很容易理解到,水不断来的时候,通过你节流阀,就可以控制你自己的实际流量,单位时间内怎样流出。

举个实际例子,例如我们需要监听窗口的大小变化来改变另一个东西的样式,那么我们肯定监听resize来触发我们的函数,但是实际上,resize触发函数会在一秒内触发很多次,难道一秒内我们就要执行函数这么多次了吗?显示,这不是我们想要的,可能我们想要的只是300ms执行一次就够了。

好了,这就不难理解这个函数做了什么工作了。

废话少说,直接开写,首先肯定需要传入执行的函数,还有我们知道需要控制某个时间内只执行一次,那会有一个interval参数,那先写个开头:

function throttle(fn, interval) {
  let _fn = fn  //保存函数的引用
  return function(){
    let _self = this
    let args = arguments
    setTimeout(function(){
      _fn.apply(_self, args)
    }, interval)
  }
}

这个开头和debounce开头一样,关键就是后面的逻辑了,也是和debounce的区别,我们想要这个函数在某个时间段只执行一次,如果在这个时间段内继续触发函数,则不执行,那么就可以增加代码为:

function throttle(fn, interval) {
  let _fn = fn  //保存函数的引用
  let timer
  return function(){
    //函数还没执行,则直接返回
    if(timer){
      return
    }
    let _self = this
    let args = arguments
    timer = setTimeout(function(){
      _fn.apply(_self, args)
      clearTimeout(timer)
      timer = null  //注意执行完要清空timer
    }, interval)
  }
}

好了,我们发觉,那如果第一次函数被触发呢?不应该直接返回吧,第一次那肯定立即要执行,不用延时吧,ok,再加个标识符:

function throttle(fn, interval) {
  let _fn = fn  //保存函数的引用
  let timer,
      firstTime = true
  return function(){
    let _self = this
    let args = arguments
    //判断第一次进来就立即执行
    if(firstTime){
      _fn.apply(_self, args)
      firstTime = false
      return
    }
    //函数还没执行,则直接返回
    if(timer){
      return
    }
    timer = setTimeout(function(){
      _fn.apply(_self, args)
      clearTimeout(timer)
      timer = null  //注意执行完要清空timer
    }, interval || 500)
  }
}

其实具体的debounce和throttle函数实现起来有很多种写法,但是都大同小异,因为基本原理都是一样的,做了各自不同扩充而已。其实理解debounce和throttle的区别很容易,debounce就相当于以触发动作为参考,延时了设定的时间就执行一次函数,如果动作在设定的时间内仍然继续触发,那么继续以新触发的动作为参考;而throttle就是第一次触发就立即执行了,然后以执行的时间为参考,延时了设定的时间之后再触发就再执行,但是如果下一次触发仍在前一次执行的设定延时时间内,则不执行了,大概,它们就是如此。

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据