手写防抖

102 阅读1分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,      点击了解详情一起参与。

【若川视野 x 源码共读】第25期 | 跟着underscore学防抖点击了解本期详情一起参与

1. 描述

简单看一下事件重复触发的场景。

index.html

<!DOCTYPE html>
<html lang="zh-cmn-Hans">

<head>
 <meta charset="utf-8">
 <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
 <title>debounce</title>
 <style>
  #container{
   width: 100%;
   height: 200px;
   line-height: 200px;
   text-align: center;
   color: #fff;
   background-color: #444; 
   font-size: 30px;
  }
 </style>
</head>

<body>
<div id="container"></div>
<script src="./debounce.js"></script>
</body>

</html>

debounce.js

let count = 1
let container = document.querySelector('#container')

function getUserAction() {
  container.innerHTML = count++
  return 22
}

container.onmousemove = getUserAction

看一下效果,从左到右执行了 384 次。

debounce2.gif

如何防止被重复触发,解决方案有两种:

  1. debounce 防抖
  2. throttle 节流

2.防抖

2.1 满足基本防抖要求

适配了 this 指针,参数基本功能。

function debounce(func, awaitTime = 1000) {
  let timeoutId = void 0
  return function() {
    let that = this
    let thatArg = arguments
    clearTimeout(timeoutId)

    timeoutId = setTimeout(() => {
      func.apply(that, thatArg)
    }, awaitTime)
  }
}

container.onmousemove = debounce(getUserAction, 1000)

项目中,有时候是需要立即执行。

2.2 增加立即执行可选配置

function debounce(func, awaitTime = 1000, immediate = true) {
  let timeoutId = void 0
  return function() {
    let that = this
    let thatArg = arguments
    clearTimeout(timeoutId)

    if (immediate) {
      let callNow = !timeoutId
      timeoutId = setTimeout(() => {
        timeoutId = void 0
      }, awaitTime)
      if (callNow) {
        func.apply(that, thatArg)
      }
    } else {
      timeoutId = setTimeout(() => {
        func.apply(that, thatArg)
      }, awaitTime)
    }
  }
}

container.onmousemove = debounce(getUserAction, 1000)

3. 总结

项目中对于经常触发的事件,比如 change , keyup , keydown 还是很有必要防抖的,而本文的防抖代码适用场景也是针对事件重复触发的情况。

本次主要是研究防抖,节流会写在下一篇。