函数防抖

  • 定义:触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会重新计算函数延长执行的时间。
  • 个人总结:使重复执行的函数在短时间内只执行最后一次。
  • 实现:
    • 准备一个定时器,把要做的事情放在定时器里;
    • 判断是否存在定时器,没有就开始计时,有就重新计时;
    • 一直重新计时,直到没有操作的时候就只有一个定时器,所以只执行一次;
  • 举个栗子
    • 没有使用防抖的时候
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13

      <!-- 给div高度撑出滚动条 -->
      <div style="height: 8000px;width: 100px;"></div>
      <script>
      const showLocaltion = () => {
      // 获取滚动条位置(兼容写法)
      var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
      // 没有防抖的时候按方向键也会执行7、8次
      console.log('滚动条位置:', scrollTop)
      }
      // 事件监听
      window.addEventListener('scroll',showLocaltion)
      </script>
    • 使用防抖
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <!-- 滚动条不停滚动,只打印一次滚动条位置-->
      <div style="height: 8000px;width: 100px;"></div>
      <script>
      const showLocaltion = () => {
      // 获取滚动条位置(兼容写法)
      var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
      // 按方向键也会执行7、8次
      console.log('滚动条位置:', scrollTop)
      }
      // 事件监听
      // 存储定时器ID
      let timer = null
      window.addEventListener('scroll', () => {
      if (timer) {
      // 有定时器就先清除再重新开始计时
      clearTimeout(timer)
      timer = setTimeout(showLocaltion, 1000)
      } else {
      // 没有定时器就直接开始计时
      timer = setTimeout(showLocaltion, 1000)
      }
      })
      </script>

    • 因为得有个外层变量存储定时器ID,所以也可以使用闭包写法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <div style="height: 8000px;width: 200px;"></div>
      <script>
      const showLocaltion = () => {
      // 获取滚动条位置(兼容写法)
      var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
      // 按方向键也会执行7、8次
      console.log('滚动条位置:', scrollTop)
      }


      function debounce(fn, delay) {
      let timer = null
      return function () {
      if (timer) {
      clearTimeout(timer)
      timer = setTimeout(fn, delay)
      } else {
      timer = setTimeout(fn, delay)
      }
      }
      }

      window.onscroll = debounce(showLocaltion, 1000)
      </script>

函数节流

  • 定义:如果短时间内大量触发同一事件,那么在事件处理函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。

  • 个人总结:使重复执行的函数每隔一段时间执行一次。

  • 实现:

    • 准备一个开关,事件处理程序根据开关来决定是否执行
    • 开始执行函数之前关闭开关
    • 每隔一段时间之后就打开开关
  • 举个栗子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <!-- 函数节流 -->
    <!-- 滚动条不停滚动,每隔一秒打印一次位置 -->
    <!-- 准备一个开关,事件处理程序根据开关来决定是否执行 -->
    <!-- 撑出滚动条 -->
    <div style="height: 8000px;width: 100px;"></div>
    <script>
    const showLocaltion = () => {
    var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
    console.log('滚动条位置:', scrollTop)
    // 隔段事件后打开开关,下次才能进入该分支
    isGo = true
    }
    // 事件监听
    // 开关
    var isGo = true
    window.addEventListener('scroll', () => {
    if (isGo) {
    // 关掉开关
    isGo = false
    setTimeout(showLocaltion, 1000)
    } else {
    return
    }
    })


    </script>
    • 闭包写法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      <div style="height: 8000px;width: 200px;"></div>
      <script>
      const showLocaltion = () => {
      var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
      console.log('滚动条位置:', scrollTop)
      // 隔段事件后打开开关,下次才能进入该分支
      isGo = true
      }


      function throttle(fn, delay) {
      let isGo = true
      return function () {
      if (isGo) {
      // 关掉开关
      isGo = false
      setTimeout(()=>{
      fn()
      // 隔段事件后打开开关,下次才能进入该分支
      isGo = true
      }, delay)
      }else{
      return
      }
      }
      }

      window.onscroll = throttle(showLocaltion, 1000)
      </script>

应用场景

节流(throttle)

  • DOM元素的拖拽功能实现(mousemove)
  • 搜索联想(keyup)
  • 计算鼠标移动的距离(mousemove)
  • Canvas 模拟画板功能(mousemove)
  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
  • 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次

防抖(debounce)

  • scroll事件滚动触发事件
  • 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
  • 表单验证
  • 按钮提交事件。
  • 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。