防抖原理

事件响应函数在一段时间后才执行,如果这段时间内再次调用,则重新计算执行时间
也就是说,在这个时间内,无论你怎么触发事件,我都不会执行,只有这段时间无操作后才会执行
作用:降低函数的触发频率,提高性能,避免资源浪费

例如:

假如防抖设置为3秒
第一次执行事件后,开始倒计时3秒
然后等到还剩余1秒时,再次触发了事件
则重新倒计时,从三秒开始
这样第一次到第二次触发事件就经历了5秒,极大的减轻了服务器的压力

先来认识防抖函数的使用:
防抖函数实际上已经封装好了,我们只需要用cdn引入就可以进行使用了

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. <style>
  8. #container {
  9. width: 100%;
  10. height: 200px;
  11. line-height: 200px;
  12. text-align: center;
  13. color: #fff;
  14. background-color: #444;
  15. background-size: 30px;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <div id="container"></div>
  21. <script src="https://cdn.bootcdn.net/ajax/libs/underscore.js/1.11.0/underscore.js"></script>
  22. <script src="./debounce.js"></script>
  23. <script>
  24. let count = 0
  25. // 展示事件频繁发生
  26. let container = document.querySelector('#container')
  27. function doSomething(e) {
  28. // event
  29. console.log(e)
  30. console.log(this)
  31. // 这里可能是回调或者是ajax请求
  32. // 这里做个示例
  33. container.innerHTML = count++
  34. }
  35. // !无防抖,数字会不停的增加,相当于不断的发送请求
  36. // container.onmousemove = doSomething
  37. // 使用防抖,300ms内不会重复发出请求
  38. container.onmousemove = _.debounce(doSomething, 300)

手动实现防抖函数

// 简单构造出防抖大致框架
// func 要执行的函数,wait要等待的时间
function debounce(func, wait) {
  let timeout
  return function () {
    // 清空定时器
    if(timeout) {
        clearTimeout(timeout)
    }
    timeout = setTimeout(func, wait)
  }

}

这样一个最最最基础的防抖就搭建好了,并没有什么难度,只需要规定时间不执行函数就可以啦!
此时防抖函数已经起作用啦,但还有一些问题需要解决

function doSomething(e) {
      // event
      console.log(e);   //MouseEvent
      console.log(this)   //window
      // 这里可能是回调或者是ajax请求
      // 这里做个示例
      container.innerHTML = count++
    }

我们此次需要解决两个问题,this和e的指向问题.
dom节点在触发事件的时候,this是指向本身的,在这里理论上是应该指向container的
但是很遗憾经过匿名函数的包裹,this指向了window,所有我们需要修复它,当然也比较简单,在匿名函数前,this是指向container的,所以我们只需要在匿名函数之前将this保存即可

function debounce(func, wait) {
  let timeout
  return function () {
    //保存this
    let _this = this
    // 清空定时器
    if(timeout) {
        clearTimeout(timeout)
    }
    timeout = setTimeout(function () {
      // console.log(this)  // 这里面的this指向window,也就是前面的count那的this是指向window
      // 但是防抖函数的this应该是指向container
      func.apply(_this)
    }, wait)
  }

}

事件处理函数有event对象,然后在测试中,e却是undefined,所以我们需要修正它为mouseEvennt

function doSomething(e) {
      // event
      console.log(e);   //undefined
      console.log(this)   //window
      container.innerHTML = count++
    }

修改:

function debounce(func, wait) {
  let timeout;
  return function () {
    // console.log(this);  //=>从中可以测试出this指向的container
    //保存this
    let _this = this;
    // 解决前面的event指向问题
    let args = arguments;
    // 清空上从定时器
    clearTimeout(timeout);
    timeout = setTimeout(function () {
      // console.log(this)  //=>这里面的this指向window,也就是前面的count那的this是指向window
      //但是防抖函数的this应该是指向container
      func.apply(_this, args);

    }, wait)
  }

}

event已经是指向mouseEvent了.这和防抖的源码已经相同啦,这时已经可以解决大多数问题了!

防抖函数常见应用

  1. scroll事件的滚动触发
  2. 搜索框输入查询
  3. 表单验证
  4. 按钮提交事件
  5. 浏览器`窗口缩放,resize事件