手写防抖节流

一、你是如何实现防抖的?讲一下思路。

拿坐电梯来说,我们知道电梯关门的机制是持续的一段时间内都没有物体经过门口,假设是5秒,5秒之内都没有物体在门口才会关门,如果从最后一次有物体经过门口的5秒之内又有物体出现,那就重新计时5秒,再重新判断能否关门。这就是防抖。放到js里说就是,你尽管触发事件,但是我一定只在你最后一次事件触发后的 n 秒才执行。

普通版

首先考虑接收的参数,一个是目标函数fn,一个是延迟时间delay;第二个考虑返回值,最终我们返回结果是要绑定对应的事件的, 所以返回值一定是一个新的函数。内部实现的思路是:

  • 我们可以在debounce函数中开启个定时器, 定时器的延迟时间就是delay

  • 并且每开启一个定时器, 我们都需要先将上一次的定时器取消掉,也就是事先要执行一个判断,如果timer有值的话就置为null;在定时器执行完之后,也要将timer置空

function mydebounce(fn, delay) {
  // 1.创建一个变量, 用于记录上一次定时器
  let timer = null

  // 2.触发事件时执行的函数
  const _debounce = function() {
    // 2.1第一次timer为null, 所有我们需要判断timer有值时清除定时器
    if (timer) clearTimeout(timer)
    // 2.2延迟执行传入的fn回调
    timer = setTimeout(() => {
      fn.apply(this)
      // 2.3函数执行完成后, 我们需要将timer重置
      timer = null
    }, delay)
  }
  // 返回一个新的函数
  return _debounce
}

进阶版-增加取消操作:例如用户在表单输入的过程中,返回了上一层页面或者关闭了页面, 就意味着我们这次延迟的网络请求没有必要继续发生了

// 用函数表达式声明函数,然后给_debounce添加一个取消防抖的方法:直接将存在的timer清空就行
  _debounce.cancel = function() {
    if (timer) clearTimeout(timer)
  }

进阶版-增加立即执行功能:有些场景需要第一次输入时, 立即执行, 后面的输入再使用防抖延迟执行--如果需要此功能, 那么传入第三个参数 immediate 参数来设定我们防抖函数是否需要立刻执行;还需要在 函数中定一个标识flag ,这个标识记录函数是否被立即执行过,然后根据这个变量去完成我们的需求

定义一个 flag 去做是否立即执行的开关逻辑,这里直接用传入进来的 immediate 不就好了吗,同样可以达到效果,为何要多此一举?

尽量不要修改传给函数的形参值,因为这写值是调用函数传进来的,如果直接修改就违反了单向传递数据流的设计了;如果因为内部的修改导致外部变量变化,说不定还会出现BUG,所以最好函数内定义自己的变量去维护里面的逻辑

// 1.设置第三个传入的参数, 并且默认值为false
function mydebounce(fn, delay, immediate = false) {
  let timer = null
  // 2.定义变量, 用于记录状态
  let isInvoke = false

  const _debounce = function(...args) {
    if (timer) clearTimeout(timer)

    // 3.第一次执行不需要延迟
    if (!isInvoke && immediate) {
      fn.apply(this, args)
      isInvoke = true //表示立即执行过了
      return
    }

    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
      // 4.重置isInvoke
      isInvoke = false //表示接下来的第一次还是要立即执行
    }, delay)
  }

  _debounce.cancel = function() {
    if (timer) clearTimeout(timer)
    // 取消也需要重置
    timer = null
    isInvoke = false
  }

  // 返回一个新的函数
  return _debounce
}

二、节流

还是拿坐电梯来说,从第一个人上电梯开始,经过5秒,电梯门都会关闭,不管上去多少人。这就是节流。

实现节流函数, 我们使用定时器是不方便管理的, 实现节流函数我们采用时间戳

我们获取一个当前时间nowTime, 我们使用new Date().gettime()方法获取, 在设定一个开始时间startTime, 等待时间waitTime

waitTime = interval - (nowTime - startTime), 当前的时间减去开始的时间得到结果, 再使用间隔时间减去这个结果, 就可以得到等待时间

得到等待时间我们在进行判断, 如果等待时间小于等于0, 那么就可以执行回调函数

开始时间startTime我们初始值为0就好, 当第一次执行时, nowTime获取的时间戳是一个非常大的值, 得到的结果waitTime是负值, 所以第一次执行节流函数, 一定会立即执行, 这也符合我们要封装的效果

function mythrottle(fn, interval) {
  // 1.定义变量保记录开始时间
  let startTime = 0

  const _throttle = function() {
    // 2. 获取当前时间
    const nowTime = new Date().getTime()
    // 3.计算需要等待的时间
    const waitTime = interval - (nowTime - startTime)

    // 4.当等待的时间小于等于0时, 执行回调函数 
    if (waitTime <= 0) {
      fn()
      // 并让开始时间等于现在时间 
      startTime = nowTime
    }
  }

  return _throttle
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值