web前端面试--防抖的原理以及实现(附带源码)

web面试题

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

web面试题专栏:点击此处

在这里插入图片描述



场景分析

百度首页的输入框input,是不是随着你的输入而实时更新最新的下拉可选项,这个如果不做防抖,那是不是敲下一个键盘就需要发起一次请求。


防抖的原理

防抖就是单位时间内,如果只有一个操作,时间到了就执行;如果在单位时间内,你又操作了,那就需要重新计时,以保证最后一次操作是使用者本身已经确定完毕的。

就比如上方的input,延迟500毫秒为例子。

你如果500毫秒内,没有再次输入,那是不是意味着,可以向服务器发起请求了;但是但在100毫秒的时候,你又输了一个字符,那是不是意味着,你还没有确定,这个时候又重新开始计时


防抖的实现

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

1. 不做防抖的例子

这里不做防抖,方便对比~

// dom
<input id="input1" type="text" />
// js
let input1 = document.getElementById("input1");
input1.addEventListener("input", function (e) {
  // 不能使用箭头函数,避免作用域提升
  console.log("第一个不做防抖的输入框:", this.value);
});

2. 全局timer做防抖的例子

这里用setTimeout来判断是否达到运行时间了,但是用了全局timer来标识。

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

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

// dom
<input id="input2" type="text" />
// js
let input2 = document.getElementById("input2");
let timer = null;
input2.addEventListener("input", function (e) {
  // 如果已经有事件了,则清空计时器重新执行新的,保证是最新一次执行
  if (timer != null) {
    clearTimeout(timer);
  }
  // 这里需要箭头函数,这样就可以提升到input2这个作用域
  timer = setTimeout(() => {
  	console.log(e);
    console.log("全局timer制作的防抖:", this.value);
  }, 1000);
});

3. 闭包做防抖的例子

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

// dom
<input id="input3" type="text" />
// js
let input3 = document.getElementById("input3");
input3.addEventListener(
  "input",
  // 这边新建了一个闭包,返回的是一个函数,保存了改作用域的变量timer
  // 这里的e,不是通过addEventListener调用传入的,是addEventListener调用了闭包,往闭包里面传参了
  // 闭包再通过call传递参数
  debounce(function (e) {
    console.log("e:", e);
    console.log("闭包做的防抖 input3:", this.value);
  }, 1000)
);

/**
  * @author: Penk
  * @description: 防抖,通过闭包保存timer变量
  * @param {*} fn
  * @param {*} timeout
  * @return {*}
  */
 function debounce(fn, timeout) {
   let timer = null;
   return function (e) {
     // 如果已经有事件了,则清空计时器重新执行新的,保证是最新一次执行
     if (timer != null) {
       clearTimeout(timer);
     }
     // 这里需要箭头函数,这样就可以提升到input2这个作用域
     timer = setTimeout(() => {
       fn.call(this);
     }, timeout);
   };
 }

源码

<!--
 * @Author: Penk
 * @LastEditors: Penk
 * @LastEditTime: 2022-11-11 20:15:52
 * @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>js防抖</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        padding: 20px;
      }

      .debounceWarper .debounceItem {
        display: flex;
        margin-top: 20px;
      }
      .debounceWarper .debounceItem .debounceItem_label {
        width: 230px;
      }

      .tooltips {
        display: block;
        color: red;
        font-size: 12px;
        margin-top: 2px;
      }
    </style>
  </head>
  <body>
    防抖:防抖就是在单位时间内只执行最后一次操作
    <!-- 防抖:控制高频事件 -->
    <div class="debounceWarper">
      <div class="debounceItem">
        <span class="debounceItem_label">不做防抖的输入框:</span>
        <input class="debounceItem_value" id="input1" type="text" />
      </div>
      <div class="debounceItem">
        <span class="debounceItem_label">全局timer做防抖的输入框:</span>
        <input class="debounceItem_value" id="input2" type="text" />
      </div>
      <span class="tooltips"
        >这里用setTimeout来判断是否达到运行时间了,但是用了全局timer来标识。</span
      >
      <span class="tooltips"
        >思考:1.如果有多个该怎么样,要怎么封装才好呢?</span
      >
      <span class="tooltips"
        >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        2.另外,业务跟功能夹杂在一起了,能不能抽取出防抖这个功能呢?</span
      >

      <div class="debounceItem">
        <span class="debounceItem_label">闭包做防抖的输入框:</span>
        <input class="debounceItem_value" id="input3" type="text" />
      </div>
      <div class="debounceItem">
        <span class="debounceItem_label">闭包做防抖的输入框:</span>
        <input class="debounceItem_value" id="input4" type="text" />
      </div>
    </div>

    <script>
      // -----------------------不做防抖的输入框:---------------------- //
      let input1 = document.getElementById("input1");
      input1.addEventListener("input", function (e) {
        // 不能使用箭头函数,避免作用域提升
        console.log("第一个不做防抖的输入框:", this.value);
      });

      // -----------------------全局timer做防抖的输入框:---------------------- //
      let input2 = document.getElementById("input2");
      let timer = null;
      input2.addEventListener("input", function (e) {
        // 如果已经有事件了,则清空计时器重新执行新的,保证是最新一次执行
        if (timer != null) {
          clearTimeout(timer);
        }
        // 这里需要箭头函数,这样就可以提升到input2这个作用域
        timer = setTimeout(() => {
          console.log("e:", e);
          console.log("全局timer制作的防抖:", this.value);
        }, 1000);
      });

      // -----------------------闭包做防抖的输入框:---------------------- //
      let input3 = document.getElementById("input3");
      input3.addEventListener(
        "input",
        // 这边新建了一个闭包
        debounce(function (e) {
          console.log("e:", e);
          console.log("闭包做的防抖 input3:", this.value);
        }, 1000)
      );

      let input4 = document.getElementById("input4");
      input4.addEventListener(
        "input",
        // 这边新建了另外一个闭包
        debounce(function (e) {
          // console.log("e:", e);
          console.log("闭包做的防抖 input4:", this, this.value);
        }, 1000)
      );

      /**
       * @author: Penk
       * @description: 防抖,通过闭包保存timer变量
       * @param {*} fn
       * @param {*} timeout
       * @return {*}
       */
      function debounce(fn, timeout) {
        let timer = null;
        return function (e) {
          // 如果已经有事件了,则清空计时器重新执行新的,保证是最新一次执行
          if (timer != null) {
            clearTimeout(timer);
          }
          // 这里需要箭头函数,这样就可以提升到input2这个作用域
          timer = setTimeout(() => {
            fn.call(this,e);
          }, timeout);
        };
      }
    </script>
  </body>
</html>

源码页面展示图

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Penk是个码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值