函数防抖与节流

1,056 阅读4分钟

浅谈js中的函数防抖与节流

前言

在平常的前端业务开发中,大家都会涉及到点击事件,以及浏览器一些滚动事件,当用户频繁操作时代码性能较低,加重浏览器的负担,导致用户体验较差,如果我们想降低这些事件的擦操作频率,我们就会用到函数的防抖与节流,其实两个函数本质都是为了降频,接下来我们将一步步实现函数的防抖与节流

函数防抖(debounce)

解释:防止“帕金森”,在频率触发的模式下,我们只识别一次,接下来将从判断参数类型,定时器的设置与清除,是否立即触发,传入的原生事件
首先看下函数的整体结构

let box = document.querySelector('.box')
/**
   *@param
        func[function,required] :最后要执行的函数
        wait[number]:设置的触发频率,默认300ms
        immediate[boolean]:设置是否开启边界触发,默认值为false
    @return
        func的返回结果
   */
function debounce (func, wait, immediate) {
  return function proxy () {
  // 核心代码
  }
}
function fn() {
  console.log('OK')
}
// 疯狂点击box,疯狂触发proxy,但是我们最终想执行fn,所以需要我们在proxy中基于一些逻辑的处理,让fn执行一次即可
box.onclick = debounce(fn)
  • 判断参数类型 当使用debounce函数时分别进行下列情况传参时,进行相应的判断
    debounce(fn)
    debounce(fn,300)
    debounce(fn,true)
    debounce(fn,300,true)

  • 定时器的设置与清除 在proxy中设置定时器,让fn执行一次

  • 是否立即触发 第一点击的时候就会触发func
  • 传入事件对象 当box.onClick = function (ev) {}这里的ev就是浏览器默认传入的

附上完整代码

function debounce (func, wait, immediate) {
  if (typeof func !== 'function') throw new TypeError("func must be a function")  // 传入参数的判断
  if (typeof wait === 'boolean') {
    immediate = true
    wait = 300
  }
  if (typeof immediate !== 'boolean') immediate = false
  if (typeof wait !== 'number') wait = 300
  var timer = null
  result = null
  return function proxy () {   // 定时器的设置与清除
    // 核心代码
    var nowRun = !timer && immediate // 声明变量检测是否立即执行
       params = [].slice.call(arguments)  // 浏览器传入的事件对象
    var self = this
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      if (timer) {
        clearTimeout(timer)
        timer = null
      }
     !immediate ? result = func().apply(self.params) : null // 边缘执行
    }, wait);
    nowRun ?result = func().apply(self.params) : null   // 立即执行
    // 返回最终结果V
    return resultV
  }
}

到此函数的防抖(debounce)已经写完,如果使用Vue开发可以开发成指令使用会更加方便(后期应该会整理出Vue开发中常用的自定义指令),在此不再赘述,接下来是函数的节流(throttle)

函数节流(throttle)

解释:降低触发的频率,它能识别多次,例如浏览器的滚动事件的触发频率过高,我们就可以使用节流函数进行降频
谷歌5-7ms,IE:10-17ms,这样我们在疯狂操作下,谷歌浏览器的频率是5ms执行一次,节流是降低这个频率,比如我们降低到300ms,在疯狂触发下,我们间接控制间隔300ms才执行一次)
节流的大致结构与防抖差不多的,只不过我们的传参不再判断是否立即执行了,我们通过滚动事件来进行演示节流效果

  • 基本结构
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="wlabelth=device-wlabelth, initial-scale=1.0">
  <title>节流函数的演示</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    html, body {
      height: 500%;
      background:-webkit-linear-gradient(top left, black, pink, blue);
    }
  </style>
</head>
<body>
  <script>
  
    /**
     *@param
          func[function,required] :最后要执行的函数
          wait[number]:设置的触发频率,默认300ms
      @return
          func的返回结果
     */
    function throttle (func, wait) {
      return function proxy () {
        // 核心代码
      }
    }

    function fn() {
      console.log('OK');
     }
     window.onscroll = debounce(fn,500)
  </script>
</body>
</html>

页面滚动后fn执行频率很高(如下图),性能较低,接下来我们通过节流函数进行优化

通过参数校验,时间戳方式,传入原生事件对象这三个方面进行实现节流函数,这里之所以不再判断是否立即执行是因为节流本身就能触发很多次,所以不再考虑了

  • 参数校验
  • 时间戳方式
  • 传入原生事件对象

节流效果展示

附上完整代码

function debounce (func, wait) {
  if (typeof func !== 'function') throw new TypeError("fun must be a function")
  if (typeof wait !== 'number') wait = 300
  var timer = null
  previous = 0 // 上一次触发的时间
  result = null
  return function proxy () {
    // 核心代码
    var now = +new Date()
    self = this
    params = [].slice.call(arguments)
    remaining = wait -(now - previous) // 距离传入的wait的时间还差多少ms
    if(remaining <= 0){ // 如果差值小于0说明已经超过了传入的时间,直接执行函数
       if(timer) {
         clearTimeout(timer)
         timer = null
        }
       result = func.apply(self,params)
       previous = +new Date() // 应该在每次函数执行后更新上一次点击的时间
     }else if(!timer) { // 如果大于0且之前没有设置过定时器,那我们设置定时器,如果已经存在定时器了,直接等待remaining后即可
       timer = setTimeout(function() {
         if(timer) {
           clearTimeout(timer)
           timer = null
         }
         result = func.apply(self,params)
         previous = +new Date() // 应该在每次函数执行后更新上一次点击的时间
       },remaining)
     }
  return result
  }
}

综上是我对节流与防抖的总结,能力有限,如有错误,望不吝指教,最后送上一句话:

夫学须静也,才须学也,非学无以广才,非志无以成学。淫慢则不能励精,险躁则不能治性。年与时驰,意与日去,遂成枯落