web前端面试–节流的原理以及实现(附带源码)

导读:本篇文章讲解 web前端面试–节流的原理以及实现(附带源码),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

web面试题

本人是一个web前端开发工程师,主要是vue框架,整理了一些面试题,今后也会一直更新,有好题目的同学欢迎评论区分享 ;-)

web面试题专栏:点击此处

请添加图片描述



场景分析

移动端web页面,需要实现滚动加载,如果一直监听滚动事件,触发滚动事件,则向后端发送数据请求。


节流的原理

限制单位时间内可以执行操作的次数。
一开始,只要触发了,就开始计时,在单位时间内,无论再次触发也不会执行操作,只有等到单位时间结束,执行了操作,才会开始新的一轮。

就以鼠标在div上移动,实时监听坐标信息,延迟500毫秒为例子。

监听移动事件是一直在运行的。
最初,开始监听移动事件,第一次触发了滚动事件,就会进入500毫秒的计时,在这500毫秒内,无法再怎么移动,都不会打印坐标信息;等到了500毫秒结束后,才会打印坐标信息。然后监听移动事件又开始新的一轮...


节流的实现

这里只做部分代码的抽取,源码在最后面,可运行的HTML文件。

1. 不做节流的例子

这里不做节流,方便对比~

// dom
<div class="throttle_box">
	<span class="throttle_box_label">不做节流:</span>
	<div id="moveBox1" class="moveBox"></div>
</div>
// js
var moveBox1 = document.getElementById("moveBox1");
moveBox1.addEventListener("mousemove", function (e) {
  console.log(`不做节流:e.screenX:${e.screenX}`);
});

2. 全局标志位isRunning做节流的例子

这里用isRunning来判断是否当前是否在运行中,是的话,就要运行结束,下次触发才生效。

思考1:如果有多个需求要节流处理,是不是有好多个isRunningX变量?要怎么封装才好呢?
思考2:另外,业务跟功能夹杂在一起了,能不能抽取出节流这个功能呢?

注意:打印事件对象e!!! 闭包不能直接获取到~

// dom
<div class="throttle_box">
  <span class="throttle_box_label">全局标志位isRunning实现节流:</span>
  <div id="moveBox2" class="moveBox"></div>
</div>
// 标志位,判断是否运行中...
// 如果是在运行了,则跳过业务逻辑;
// 如果不在运行,则将状态改成在运行中,并且在业务逻辑处理完毕后,需要将状态改成不在运行中...
let isRunning = false;
var moveBox2 = document.getElementById("moveBox2");
moveBox2.addEventListener("mousemove", function (e) {
  // 不在运行中
  if (!isRunning) {
    setTimeout(() => {
      console.log(`全局标志位isRunning实现节流:e.screenX:${e.screenX}`);
      // 运行完毕需要关闭正在运行的标志
      isRunning = false;
    }, 500);

    isRunning = true;
  }
});

3. 闭包做节流的例子

这个东西比较难理解,不懂的同学可以先看看闭包~
注意1:this的作用域问题,需要用call来处理
注意2:事件e要怎么在闭包中传递

// dom
<div class="throttle_box">
  <span class="throttle_box_label">闭包实现节流:</span>
  <div id="moveBox3" class="moveBox"></div>
</div>
// js
var moveBox3 = document.getElementById("moveBox3");
moveBox3.addEventListener(
  "mousemove",
  // 改参数是一个函数
  // 应用闭包,返回的函数自带作用域私有变量
  throttle((e) => {
    console.log(`闭包实现节流 input3:e.screenX:${e.screenX}`);
  }, 500)
);


/**
 * @author: Penk
 * @description: 节流函数
 * @param {*} fn
 * @param {*} timeout
 * @return {*}
 */
function throttle(fn, timeout = 500) {
  let isRunning = false;
  // 这里的event,可以通过.call 传过去到fn参数中
  return function (e) {
    if (!isRunning) {
      setTimeout(() => {
        fn.call(this, e);
        isRunning = false;
      }, timeout);
      isRunning = true;
    }
  };
}

源码

<!--
 * @Author: Penk
 * @LastEditors: Penk
 * @LastEditTime: 2022-11-23 02:28:41
 * @FilePath: \web面试题\节流.html
 * @email: 492934056@qq.com
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        width: 100%;
        height: 100vh;
        padding: 20px;
        box-sizing: border-box;
      }
      .moveBox {
        width: 500px;
        height: 200px;
        background-color: antiquewhite;
      }

      .throttle_box .throttle_box_label {
        display: block;
        margin: 30px 0 10px 0;
      }

      .tooltips {
        display: block;
        color: red;
        font-size: 12px;
        margin-top: 2px;
      }
    </style>
  </head>
  <body>
    <h3>这边制作的是一个div,监听器鼠标在div上滑动然后就调用异步方法。</h3>

    <div class="throttle_box">
      <span class="throttle_box_label">不做节流:</span>
      <div id="moveBox1" class="moveBox"></div>
    </div>
    <div class="throttle_box">
      <span class="throttle_box_label">全局标志位isRunning实现节流:</span>
      <div id="moveBox2" class="moveBox"></div>
    </div>
    <div class="throttle_box">
      <span class="throttle_box_label">闭包实现节流:</span>
      <div id="moveBox3" class="moveBox"></div>
    </div>
    <div class="throttle_box">
      <span class="throttle_box_label">闭包实现节流:</span>
      <div id="moveBox4" class="moveBox"></div>
    </div>

    <script>
      var moveBox1 = document.getElementById("moveBox1");
      moveBox1.addEventListener("mousemove", function (e) {
        console.log(`不做节流:e.screenX:${e.screenX}`);
      });

      // 标志位,判断是否运行中...
      // 如果是在运行了,则跳过业务逻辑;
      // 如果不在运行,则将状态改成在运行中,并且在业务逻辑处理完毕后,需要将状态改成不在运行中...
      let isRunning = false;
      var moveBox2 = document.getElementById("moveBox2");
      moveBox2.addEventListener("mousemove", function (e) {
        // 不在运行中
        if (!isRunning) {
          setTimeout(() => {
            console.log(`全局标志位isRunning实现节流:e.screenX:${e.screenX}`);
            // 运行完毕需要关闭正在运行的标志
            isRunning = false;
          }, 500);

          isRunning = true;
        }
      });

      var moveBox3 = document.getElementById("moveBox3");
      moveBox3.addEventListener(
        "mousemove",
        // 改参数是一个函数
        // 应用闭包,返回的函数自带作用域私有变量
        throttle((e) => {
          console.log(`闭包实现节流 input3:e.screenX:${e.screenX}`);
        }, 500)
      );

      var moveBox4 = document.getElementById("moveBox4");
      moveBox4.addEventListener(
        "mousemove",
        // 改参数是一个函数
        // 应用闭包,返回的函数自带作用域私有变量
        // 这里的e,不是事件监听直接给的,是由闭包call带参e
        throttle((e) => {
          console.log(`闭包实现节流 input4:e.screenX:${e.screenX}`);
        }, 500)
      );

      /**
       * @author: Penk
       * @description: 节流函数
       * @param {*} fn
       * @param {*} timeout
       * @return {*}
       */
      function throttle(fn, timeout = 500) {
        let isRunning = false;
        // 这里的event,可以通过.call 传过去到fn参数中
        return function (e) {
          if (!isRunning) {
            setTimeout(() => {
              fn.call(this, e);
              isRunning = false;
            }, timeout);
            isRunning = true;
          }
        };
      }
    </script>
  </body>
</html>

源码页面展示图

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/66359.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!