JavaScript中的节流和防抖

该文章已生成可运行项目,

1、节流和防抖的目的:

都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,防止在短时间内频繁触发同一事件而出现延迟,假死或卡顿的现象

2、节流和防抖的区别:

防抖:如果不断在delay之前重新触发,那么定时器会不断重新计时,最终会在最后一次完后才执行

节流:目前有一事件A设置了定时器,那么在delay之前触发,都只会触发一次

3、节流和防抖的详解

(1)、防抖 debounce(设置1分钟只会执行一次,如果1分钟内又多次触发,会从再次触发开始重新计算1分钟时间,然后再执行)

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

本质:将多次执行变为最后一次执行(重新执行)

举例:比如我们平时在使用搜索框时,我们一输入内容就会发送对应的网络请求,如果我们后面一直有在输入内容,那么就会一直发送网络请求。正确的做法应该是在我们输入期间不发送网络请求,当我们输入完成后在发送网络请求。

(2)、节流 throttle(设置1分钟只会执行一次,一分钟内,多次触发无效,必须等1分钟后才能触发函数)

高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

本质:将多次执行变成每隔一段时间执行(不能打断我)

举例:比如我们在玩LOL游戏时,当需要回城时,我们触发回城按钮,进入到回城状态进会有一个等待的时间,如果在这个等待时间内有重复去执行回城按钮,这时回城状态并不会做出其他响应,而是等时间到了后才会完成回城。这也说明了,在我们回城的过程中,一直触发回城按钮都是不会响应的,他会按照自己的一个回城时间才做出响应。

4、代码实现简单防抖与节流

4.1、防抖

(1)、简单版
<body>
  <!-- 防抖函数 debounce -->
  <input type="text" name="" id="" />

  <script>
    var int = document.querySelector("input");
    function inputChange() {
      console.log(this.value);
    }
    // 不加防抖函数,不断的输出
    //int.addEventListener("keyup", inputChange);
    
    //加入防抖函数,1秒钟输出一次
    var intVal = debounce(inputChange, 1000);
    int.addEventListener("keyup", intVal);
    
    //防抖函数
    function debounce(fn, delay) {
      // 1、定义一个定时器,保存上一次的定时器
      var timer = null;
      // 2、真正执行的函数
      var _debounce = function () {
        //3、 取消上一次的定时器
        if (timer) clearTimeout(timer);
        //4、 保存this
        var _this = this;
        //5、 延迟执行
        timer = setTimeout(function () {
          //6、 让fn执行时,指向input
          fn.call(_this);
        }, delay);
      };
      return _debounce;
    }
  </script>
</body>
(2)、this和参数实现
<input type="text" name="" id="" />
  <script>
    var int = document.querySelector("input");
    // 需求:输出输入的内容
    function inputChange() {
      console.log(this.value);
    }
    // 不加防抖函数,不停的搜索
    // int.addEventListener("keyup", inputChange);
    // 加入防抖函数
    int.addEventListener("keyup", debounce(inputChange, 1000));

    //  防抖函数二:this和参数实现

    function debounce(fn, delay) {
      //console.log(this, "debounce"); //这里的this指向的是window
      // 1、定义一个定时器,保存上一次的定时器
      var timer = null;
      // 2、真正执行的函数,传入参数
      return function (...ages) {
        //3、保存this,此时的this指向的是input
        var _this = this;
        // 4、判断定时器是否存在,清楚定时器
        if (timer) clearTimeout(timer);
        // 重新调用setTimerout
        timer = setTimeout(function () {
          //console.log(this);//定时器里的this指向widow
          // fn()直接执行,this指向window
          fn.apply(_this, ages);
          timer = null;
        }, delay);
      };
    }
</script>
(3)、立即执行
  <input type="text" name="" id="" value="你好" />
  <script>
    var int = document.querySelector("input");
    // 需求:输出输入的内容
    function inputChange() {
      console.log(this.value);
    }
    // 不加防抖函数,不停的搜索
    // int.addEventListener("keyup", inputChange);
    // 加入防抖函数
    int.addEventListener("keyup", debounce(inputChange, 1000, true));
    //  int.addEventListener("keyup", debounce(inputChange, 1000, false));
    
    //  防抖函数三:立即执行
    
    //创建一个防抖函数debounce
    function debounce(fn, delay, immediate = false) {
      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null;
      let isInvoke = false;
      // 2.真正执行的函数
      const _debounce = function (...ages) {
        // 取消上一次的定时器
        if (timer) clearTimeout(timer);
    
        // 判断是否需要立即执行
        if (immediate && !isInvoke) {
          fn.apply(this, ages);
          isInvoke = true;
        } else {
          // 延迟执行
          timer = setTimeout(() => {
            // 外部传入的真正要执行的函数
            fn.apply(this, ages);
            isInvoke = false;
          }, delay);
        }
      };
    
      return _debounce;
    }
  </script>

4.2

(1)、时间戳版
<body>
    <!-- 需求:在快速点击的过程中,降低日志打印的频率,1s中执行一次 -->

    <button>点我试试</button>
    <script>
      var btn = document.querySelector("button");
      var fn = function () {
        console.log("发送请求");
      };
      // 问题:快速点击按钮,只要点了一次,日志就打印一次
      // btn.addEventListener("click", fn);
      // 添加节流函数,在2s内,多次点击,只执行一次
      btn.addEventListener("click", throttle(fn, 2000));


      //参数:  fn 真正执行的函数,interval 多久执行一次
      function throttle(fn, interval) {
        // 1、记录上一次的开始时间
        var lastTime = 0;
        // 2、事件触发时,真正执行的函数
        var _throttle = function () {
          // 2.1获取当前事件触发时的时间
          var nowTime = new Date().getTime();
          // 2.2使用规定好的时间间隔减去当前时间和上一次触发时间的时间间隔,得到多少时间再次触发
          var remainTime = interval - (nowTime - lastTime);
          if (remainTime <= 0) {
            // 2.3触发真正函数
            fn();
            // 2.4 保留上次触发的时间
            lastTime = nowTime;
          }
        };
        return _throttle;
      }
    </script>
(2)、定时器版
 <body>
    <!-- 需求:在快速点击的过程中,降低日志打印的频率,2s中执行一次 -->
    <button>点我试试</button>
    <script>
      var btn = document.querySelector("button");
      var fn = function () {
        console.log("发送请求");
      };
      // 问题:快速点击按钮,只要点了一次,日志就打印一次
      // btn.addEventListener("click", fn);
      // 添加节流函数,在2s内,多次点击,只执行一次
      btn.addEventListener("click", throttle(fn, 2000));

      //参数:  fn 真正执行的函数,interval 多久执行一次
      // 定时器方式
      function throttle(fn, delay) {
        //1、设置标志,判断函数是否执行
        var sign = true;
        return function () {
            //2、 在函数开头判断标志是否为 true,不为 true 则中断函数
          if (!sign) return;
           //3、  sign 设置为 false,防止执行之前再被执行
          sign = false;
          //4、 保存this
          var _this = this;
          //5、 延迟执行函数,delay 时间后执行
          setTimeout(function () {
            fn.call(_this);
            sign = true;
          }, delay);
        };
      }
    </script>
  </body>

5、节流的应用场景

防抖

表单元素的校验,如手机号,邮箱,用户名等,部分搜索功能的模糊查询结果实现

搜索框搜素输入

文本编辑器实时保存

节流

高频事件,例如快速点击、鼠标滑动、resize事件、scroll事件

下拉加载

视频播放记录时间等

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值