前端性能优化篇: 防抖和节流

防抖和节流

2021/5/5

参考文章

一. 为什么要用到防抖节流

  • 当函数绑定一些持续触发的事件如:resize、scroll、mousemove ,键盘输入,多次快速click等等,

  • 如果每次触发都要执行一次函数,会带来性能下降,资源请求太频繁等问题

  • 就比如这样

    • div{
      	height:150px;
      	line-height:150px;
      	text-align:center;
      	color: #fff;
      	background-color:#ccc;
      	font-size:80px;
      }
      复制代码
    • <div id="content"></div>
        <script>
          let num = 1;
          const content = document.getElementById('content');
          function count() {
            content.innerHTML = num++;
          };
          content.onmousemove = count;
        </script>
      
      复制代码
  • 上面的效果,只要鼠标在div区域内一移动,count函数就会被执行,数字就会增加。

  • 这时就可以用上防抖和节流了

二. 防抖

1. 什么是防抖?

  • 所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

2. 应用防抖

非立即执行版本
  • 效果就是防抖的定义

  •  let num = 1;
     const content = document.getElementById('content');
     // 功能函数
     function count() {
      this.innerHTML = num++;
     };
        
        
    content.onclick = debounce(count,1000);
    
    // 防抖函数,非立即执行版本
     function debounce(func, wait) {
      let timeout;
      return function () {
        const context = this;
        const args = [...arguments];
        if (timeout) clearTimeout(timeout);
        timeout = setTimeout(() => {
          func.apply(context, args)
        }, wait);
      }
    }
    复制代码
  • 防抖函数的代码使用这两行代码来获取 this 和 参数,是为了让 debounce 函数最终返回的函数 this 指向不变(即指向调用该函数的DOM元素)以及依旧能接受到 e 参数。

  • 功能函数通过调用apply方法将debounce 函数最终返回的函数 this 指向绑定给自身。

  • 不管是setTimeout还是setInterval,都会有一个返回值。这个返回值是一个数字,代表当前是在浏览器中设置的第几个定时器(返回的是定时器序号)。

  • 每次触发调用防抖函数,如果之前的定时器(以定时器序号作为标识符)还在。就清除前面一个定时器,并开启一个新的定时器。

  • 定时器即使清除了,其返回值也不会清除,之后设置定时器的返回值也会在其返回值的基础上继续向后排。

立即执行版
  • 触发事件后函数会立即执行,n 秒内触发事件不会执行功能函数下一次调用,n秒后再次触发才会再次执行功能函数。

  • // 防抖函数,立即执行版本
    function debounce(func, wait) {
            let timeout;
            return function () {
              const context = this;
              const args = [...arguments];
              if (timeout) clearTimeout(timeout);
              const callNow = !timeout;
              timeout = setTimeout(() => {
                timeout = null;
              }, wait);
              if (callNow) func.apply(context, args);
            };
          }
    复制代码
  • 只有在callnow值为true时才会触发功能函数

  • 第一次调用防抖函数,还没有定时器,触发功能函数

  • 在时间间隔内如果再次调用防抖函数,这时已经有定时器在了,即使清除定时器,但定时器标识符timeout还在,所以callNow的值是false,不能触发功能函数

  • 等定时器间隔过去后执行定时器里的代码,将定时器标识符timeout设置为null

  • 之后再调用防抖函数才会让callNow的值为true,触发功能函数

合并
  • 增加一个形参作为判断

  • // 防抖函数,合并版本,immediate为true时为立即执行
    function debounce(func, wait, immediate) {
      let timeout;
      return function () {
        const context = this;
        const args = [...arguments];
        if (timeout) clearTimeout(timeout);
        if (immediate) {
          const callNow = !timeout;
          timeout = setTimeout(() => {
            timeout = null;
          }, wait)
          if (callNow) func.apply(context, args)
        }
        else {
          timeout = setTimeout(() => {
            func.apply(context, args)
          }, wait);
        }
      }
    }
    
    复制代码

三. 节流

1. 什么是节流?

  • **所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。**节流会稀释函数的执行频率。

2. 应用节流

时间戳
  • function throttle(func, wait) {
        var previous = 0;
        return function() {
            let now = Date.now();
            let context = this;
            let args = arguments;
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }
    }
    content.onmousemove = throttle(count,1000);
    复制代码
  • 时间戳版的功能函数触发是在时间段内开始的时候

定时器
  • function throttle(func, wait) {
        let timeout;
        return function() {
            let context = this;
            let args = arguments;
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
    
        }
    }
    
    
    复制代码
  • 第一次调用节流函数,还没有定时器,创建一个定时器,并在时间间隔结束时触发功能函数

  • 在时间间隔内再次调用节流函数,由于定时器已经存在,不响应

  • 当时间间隔结束后将本定时器标识符timeout清除,再创建一个定时器。

  • 由于定时器标识符timeout被设置为null,再次调用节流函数便可再次触发。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享