模拟实现underscore中的节流throttle

9次阅读

共计 2143 个字符,预计需要花费 6 分钟才能阅读完成。

函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div style="height: 3000px"></div>
</body>
<script src="./throttle.js"></script>
<!-- <script src="../underscore.js"></script> -->
<script>
  var throttle = _.throttle(function() {console.log('hello throttle')
  }, 1500/* , {trailing: false} */)
  window.onscroll = throttle
  // 没有参数 表示 默认会执行两次,一次立即执行,一次等待 wait 毫秒后执行
  // leading false 表示 阻止立即执行,等待 wait 毫秒后调用
  // trailing 先立即执行,之后再触发时若过了 wait 毫秒则执行
  // 两个参数不能同时设置为 false
  // 拖拽时使用
  // 指定时间后调用
</script>
</html>

实现

var _ = {}
_.now = Date.now
_.throttle = function (func, wait, options) {
  var args, result, lastTime = 0 // 初始值
  var timeOut = null
  if (!options) {options = {}
  }
  var later = function () {
    // 执行一次之后,如果设置了 leading 为 false,需要把 lastTime 设为 0,为了能够执行 lastTime = now 
    // 没有设置则 lastTime 为当前设置为当前时间
     lastTime = options.leading === false ? 0 : _.now()
     console.log("later-lasttime:", lastTime)
     timeOut = null // 一次执行后清除定时器,为了下次能够执行
     result = func.apply(null, args) // 执行回调
     return result 
  }
  return function () {
    // 当前执行节流函数的时间
    var now = _.now()
    args = arguments
    // lastTime 为 0 表示是首次执行
    if (!lastTime && options.leading === false) { // 设置了 leading 为 false
      lastTime = now // 执行此步后 remaining 为 wait
    }
    // 配置了 Leading 首次执行 remaining 为 wait
    // now - lastTime 为两次触发节流函数之间的间隔
    // 两次之间间隔小于 wait 时,remaining 为正值
    // 配置了 trailing 直接执行下面代码,第一次 lastTime 为 0,remaining 会是负数
    // 只有当 now - lastTime 间隔大于 wait 时,remaining 才为负数
    console.log("lasttime:", lastTime)
    console.log("now:",now)
    var remaining = wait - (now - lastTime)
    console.log(timeOut)
    console.log(remaining)
    if (remaining <= 0) {
      // 设置 trailing 为 false
      if (timeOut) {clearTimeout(timeOut)
        timeOut = null
      }
      lastTime = now // 第一次执行后,把 now 赋值给 lastTime
      result = func.apply(null, args) // 立即执行
    } else if (!timeOut && options.trailing !== false) { // 设置了 leading 为 false 同时没有设置 trailing 为 false
      // timeOut 不为空 这时说明已经有定时函数,这时就不能再触发执行,如此实现了限流,即隔指定时间再执行,中间触发时都不执行
      // timeOut 为空 没有定时函数,并且 options.trailing !== false 说明没有传入 trailing 
      // 没有传入配置项时,会先立即执行过一次后,还没有定时器,会进这里
      // 在 remaining 时间段后会再执行一次,即没有配置会执行两次,一次立即执行,一次是 wait 时间后执行
      // 由于此时已经过了 now - lastTime 所以是 remaining 时间后再执行,总体来看就是 一次立即执行,一次是 wait 时间后执行
      timeOut = setTimeout(later, remaining) // 由于 leading 为 false 要实现隔 wait 时间调用,那么 remaing 值等于 wait 那么 now 与 lastTime 就要相等
    }
    return result
  }
}
正文完
 0