zoukankan      html  css  js  c++  java
  • JS的防抖函数和节流函数

    JS的防抖函数和节流函数:

    防抖目的:

    很容易理解,在项目中使用相应的触发函数的时候,会出现那种比如高频触发 搜索框实时发请求,onmousemove, resize, onscroll啥的, 我们不想这样频繁触发,只想在点击的时候才进行触发,所以我们就加以操作。进行控制

    防抖函数用法:

    such as: 我们鼠标滑动进行数据增加(案例是抄的)

    <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
     
    <script>
        let num = 1;
        let content = document.getElementById('content');
     
        function count() {
            content.innerHTML = num++;
        };
        content.onmousemove = count;
    </script>
    

    效果就是这样的 只要动鼠标就会增加。 但是我们就要在多长时间内才触发 不能频繁触发
    在这里插入图片描述

    解决方案:

    1. 非立即执行版:
      短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。
    // 非立即执行版
    function debounce(func, wait) {
        let timer;
        return function() {
          let context = this; // 注意 this 指向
          let args = arguments; // arguments中存着e
             
          if (timer) clearTimeout(timer);
     
          timer = setTimeout(() => {
            func.apply(this, args)
          }, wait)
        }
    }
    
    // 触发
    content.onmousemove = debounce(count,1000);  //参数count 指的就是运行的方法体
    
    1. 立即执行版:
      立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
    // 立即执行版
    function debounce(func, wait) {
        let timer;
        return function() {
          let context = this; // 这边的 this 指向谁?
          let args = arguments; // arguments中存着e
     
          if (timer) clearTimeout(timer);
     
          let callNow = !timer;
     
          timer = setTimeout(() => {
            timer = null;
          }, wait)
     
          if (callNow) func.apply(context, args);
        }
    }
    // 触发
    content.onmousemove = debounce(count,1000);  //参数count 指的就是运行的方法体
    
    1. 合成版:(传入true或false手动选择是立即还是非立即执行)
    // 合成版
    /**
       * @desc 函数防抖
       * @param func 目标函数
       * @param wait 延迟执行毫秒数
       * @param immediate true - 立即执行, false - 延迟执行
       */
    function debounce(func, wait, immediate) {
        let timer;
        return function() {
          let context = this,
              args = arguments;
               
          if (timer) clearTimeout(timer);
          if (immediate) {
            let callNow = !timer;
            timer = setTimeout(() => {
              timer = null;
            }, wait);
            if (callNow) func.apply(context, args);
          } else {
            timer  = setTimeout(() => {
              func.apply
            }, wait)
          }
        }
    }
    

    节流函数用法:

    节流目的:

    指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次... 。节流如字面意思,会稀释函数的执行频率。
    效果:在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次。
    在这里插入图片描述
    时间戳版本:

    // 时间戳版
    function throttle(func, wait) {
        let 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);//参数count 指的就是运行的方法体
    

    定时器版本:

    // 定时器版
    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)
          }
        }
    }
    
    //触发
    content.onmousemove = throttle(count,1000);//参数count 指的就是运行的方法体
    

    双剑合璧的版本:手动触发

    /**
     * @desc 函数节流
     * @param func 函数
     * @param wait 延迟执行毫秒数
     * @param type 1 表时间戳版,2 表定时器版
     */
    function throttle(func, wait, type) {
      if (type === 1) {
        let previous = 0;
      } else if (type === 2) {
        let timeout;
      }
      return function() {
        let context = this;
        let args = arguments;
        if (type === 1) {
            let now = Date.now();
     
            if (now - previous > wait) {
              func.apply(context, args);
              previous = now;
            }
        } else if (type === 2) {
          if (!timeout) {
            timeout = setTimeout(() => {
              timeout = null;
              func.apply(context, args)
            }, wait)
          }
        }
      }
    }
    

    注意: 关于节流/防抖函数中 context(this) 的指向解析:

      首先,在执行 throttle(count, 1000)这行代码的时候,会有一个返回值,这个返回值是一个新的匿名函数,因此 content.onmousemove = throttle(count,1000); 这句话最终可以这样理解:

    content.onmousemove = function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        ...
        console.log(this)
    }
    

      到这边为止,只是绑定了事件函数,还没有真正执行,而 this 的具体指向需要到真正运行时才能够确定下来。所以这个时候如果我们把前面的 content.onmousemove 替换成 var fn 并执行 fn fn() ,此时内部的 this 打印出来就会是 window 对象。

      其次,当我们触发 onmousemove 事件的时候,才真正执行了上述的匿名函数,即 content.onmousemove() 。此时,上述的匿名函数的执行是通过 对象.函数名() 来完成的,那么函数内部的 this 自然指向 对象。

      最后,匿名函数内部的 func 的调用方式如果是最普通的直接执行 func() ,那么 func 内部的 this 必然指向 window ,虽然在代码简单的情况下看不出什么异常(结果表现和正常一样),但是这将会是一个隐藏 bug,不得不注意啊!所以,我们通过匿名函数捕获 this,然后通过 func.apply() 的方式,来达到 content.onmousemove = func 这样的效果。

    可以说,高阶函数内部都要注意 this 的绑定。

    咫尺远近却无法靠近的那个你,才敢让你发觉你并不孤寂
  • 相关阅读:
    Hadoop Gateway 部署
    java 命令--备忘
    整理下常用硬件性能参数
    python 脚本备份 mysql 数据库到 OSS
    pip 更换国内源
    记录闭包和立即执行函数
    Django 中文乱码问题&富文本显示
    mysql exceeded the 'max_questions' resource 记录
    sql server 数据字典的妙用
    Sublime Text指南
  • 原文地址:https://www.cnblogs.com/tcz1018/p/14428851.html
Copyright © 2011-2022 走看看