使用lodash防抖_7 个角度吃透 Lodash 防抖节流原理

引言

上一节我们学习了 Lodash 中防抖和节流函数是如何实现的,并对源码浅析一二,今天这篇文章会通过七个小例子为切入点,换种方式继续解读源码。其中源码解析上篇文章已经非常详细介绍了,这里就不再重复,建议本文配合上文一起服用,猛戳这里学习 https://github.com/yygmind/blog/issues/41(点击「

有什么想法或者意见都可以在评论区留言,欢迎大家拍砖。

节流函数 Throttle

我们先来看一张图,这张图充分说明了 Throttle(节流)和 Debounce(防抖)的区别,以及在不同配置下产生的不同效果,其中 mousemove 事件每 50 ms 触发一次,即下图中的每一小隔是 50 ms。今天这篇文章就从下面这张图开始介绍。

f244fa2088cd8669a749b4965320518f.png

角度 1

lodash.throttle(fn, 200, {leading: true, trailing: true})

mousemove 第一次触发

先来看下 throttle 源码

function throttle(func, wait, options) {
    
  // 首尾调用默认为 true
  let leading = true
  let trailing = true

  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }
  // options 是否是对象
  if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading
    trailing = 'trailing' in options ? !!options.trailing : trailing
  }
  // maxWait 为 wait 的防抖函数
  return debounce(func, wait, {
    leading,
    trailing,
    'maxWait': wait,
  })
}

所以 throttle(fn, 200, {leading: true, trailing: true}) 返回内容是 debounce(fn, 200, {leading: true, trailing: true, maxWait: 200}),多了 maxWait: 200 这部分。

先打个预防针,后面即将开始比较难的部分,看下 debounce 入口函数。

// 入口函数,返回此函数
function debounced(...args) {
  // 获取当前时间
  const time = Date.now()
  // 判断此时是否应该执行 func 函数
  const isInvoking = shouldInvoke(time)

  // 赋值给闭包,用于其他函数调用
  lastArgs = args
  lastThis = this
  lastCallTime = time

  // 执行
  if (isInvoking) {
    // 无 timerId 的情况有两种:
    // 1、首次调用 
    // 2、trailingEdge 执行过函数
    if (timerId === undefined) {
      return leadingEdge(lastCallTime)
    }

    // 如果设置了最大等待时间,则立即执行 func
    // 1、开启定时器,到时间后触发 trailingEdge 这个函数。
    // 2、执行 func,并返回结果
    if (maxing) {
      // 循环定时器中处理调用
      timerId = startTimer(timerExpired, wait)
      return invokeFunc(lastCallTime)
    }
  }
  // 一种特殊情况,trailing 设置为 true 时,前一个 wait 的 trailingEdge 已经执行了函数
  // 此时函数被调用时 shouldInvoke 返回 false,所以要开启定时器
  if (timerId === undefined) {
    timerId = startTimer(timerExpired, wait)
  }
  // 不需要执行时,返回结果
  return result
}

对于 debounce(fn, 200, {leading: true, trailing: true, maxWait: 200}) 来说,会经历如下过程。

  • 1、shouldInvoke(time) 中,因为满足条件 lastCallTime === undefined,所以返回 true

  • 2、lastCallTime = time,所以 lastCallTime 等于当前时间,假设为 0

  • 3、timerId === undefined 满足,执行 leadingEdge(lastCallTime) 方法

// 执行连续事件刚开始的那次回调
function leadingEdge(time) {
  // 1、设置上一次执行 func 的时间
  lastInvokeTime = time
  // 2、开启定时器,为了事件结束后的那次回调
  timerId = startTimer(timerExpired, wait)
  // 3、如果配置了 leading 执行传入函数 func
  // leading 来源自 !!options.leading
  return leading ? invokeFunc(time) : result
}
  • 4、在 leadingEdge(time) 中,设置 lastInvokeTime 为当前时间即 0,开启 200 毫秒定时器,执行 invokeFunc(time) 并返回

// 执行 Func 函数
function invokeFunc(time) {
  // 获取上一次执行 debounced 的参数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值