js - 关于防抖和节流函数的使用和细节

news2024/12/24 8:54:05

在这里插入图片描述

文章目录

      • 一、什么是防抖
      • 二、应用场景
      • 三、实现原理
        • 1,第一个问题:为什么使用了闭包(也就是说timer为什么定义到了外面)
        • 2,第二个问题:防抖函数中this的指向问题:
      • 四、节流函数

一、什么是防抖

事件响应函数在一段时间后才执行,如果这段时间内再次调用,则重新计算执行时间。必须等待规定时间后才执行此函数;

二、应用场景

1,高频的事件监听回调,比如onscroll,onresize,oninput,touchmove等;

2,用户名,手机号,邮箱输入验证时的输入框自动补全事件,搜索框输入搜索(用户最后一次输入完成才请求数据);

3,浏览器窗口大小改变后,等待调整完成后,在执行resize里面的函数;

三、实现原理

总的来说,函数节流与函数防抖的原理非常简单,巧妙地使用 setTimeout 来存放待执行的函数,这样可以很方便的利用 clearTimeout 在合适的时机来清除待执行的函数。以及使用利用闭包形成的高级函数;

看下面简单的案例

绿色容器内监听鼠标的移动事件,如果一直移动则什么也不操作,当停下来500毫秒之后才打印 防抖触发了

在这里插入图片描述
代码如下:

<!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>
      .item {
        box-sizing: border-box;
        width: 300px;
        height: 300px;
        padding: 20px;
        margin: 10px;
        border: 8px solid red;
        background-color: yellowgreen;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="item" id="item">
      </div>
    </div>
    <script>
      let item = document.getElementById("item");
      item.addEventListener("mousemove", debounce(foo));
      // 基本的防抖函数
      function debounce(foo, delay=500) {
        let timer = null;
        return function () {
          if (timer) {
            clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
          }
          timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
        };
      }
       // 执行函数
       function foo() {
         console.log("防抖触发了");
      }
    </script>
  </body>
</html>

防抖函数基本实现

   function debounce(foo, delay = 500) {
        let timer = null;
        return function () {
          if (timer) {
            clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
          }
          timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
        };
      }

上面会实现一个基本的防抖函数,看似简单的几行代码,则会有以下的细节:

1,第一个问题:为什么使用了闭包(也就是说timer为什么定义到了外面)

先复习一下什么是闭包:

 	 function fun() {
        var str = 0;
        function sum() { //此处就形成了闭包
          return ++str; //这里使用了外部函数内的变量,此变量会一直被保存下来
        }
        return sum;
      }
      var count = fun();
      console.log(count()); // 1
      console.log(count()); // 2
      console.log(count()); // 3

闭包概念

闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。

这归功于js特有的作用域链式查找,函数内部可以向上直接读取全局变量,会一级级的向上查找,直到找到为之。(此过程不可逆)

一旦使用了闭包且访问了外部函数定义的变量,那么此变量会一直保存下来(使用过多会导致内存泄露);

而且使用闭包也能把内部定义的变量返回出去,供全局使用;

了解完闭包再回到上面的问题,为什么使用闭包,其实也就是为什么把timer这个定时器id定义到外面;

因为:

如果timer的定义放在return function函数的里面会导致 if (timer)这个条件一直不成立,这样的会启动很多定时器, 每次执行这个函数就会新增一个 setTimeout定时器,那这么多定时器就会差不多同时执行 ,达不到防抖的效果,而且还增加了js的运行负担;

我们把timer定义到了外面,这样只会同时开启一个定时器,多次进来会清除之前的定时器,而重新启动一个新的定时器,且新的定时器id一直被外面timer所保存,所以不会同时产生多个setTimeout;也就达到了防抖的效果;

至此,为什么使用闭包介绍完毕;防抖函数中因为使用了定时器,也就会产生了一个新的问题,this的指向问题;

2,第二个问题:防抖函数中this的指向问题:

见下图:

在这里插入图片描述
通过打印可以发现,第一个this是指向Window的,第二个this是指向此item节点元素的,

至于第三个定时器的this为什么也指向window:

因为

JS的定时器方法是定义在window下的,如果不使用箭头函数和主动改变this指向,setInterval和
setTimeout的回调函数中this的指向的都是Window;

如果调用防抖函数有涉及到上下文,那么就要考虑到定时器中this的指向问题。就要使用apply改变this的指向;

之前的基本的防抖函数可以优化为以下代码:

完整的防抖函数

      // 防抖
      function debounce(foo, delay = 500) {
        let timer = null;
        return function () {
          let _this = this; // 此处保留this
         
          if (timer) {
            clearTimeout(timer); 
          }
          timer = setTimeout(function(){
            foo.apply(_this) // 改变this指向
          }, delay); 
        };
      }

四、节流函数

节流函数的细节和防抖是一样的,这里就不在赘述了,主要是实现的方式有点不一样;

概念:

节流(throttel)不管事件的触发频率有多高,只会间隔设定的时间执行一次目标函数。简单来说:每隔单位时间,只执行一次。

应用场景: 滚动条滚动时函数的处理,可以通过节流适当减少响应次数;

基本实现: 基本可以满足大部分需求

   function throttle(foo, delay = 500) {
        let timer = null;
        return function () {
          if (timer) {
            return false; //定时器还在,等冷却时间
          }
          timer = setTimeout(function(){
            foo();
            clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
            timer = null;        //此处一点要清空 
          }, delay); 
        };
      }

带apply的实现:

      function throttle(foo, delay = 500) {
        let timer = null;
        return function () {
          let _this = this;
          if (timer) {
            return false; //定时器还在,等冷却时间
          }
          timer = setTimeout(function(){
            foo.apply(_this);
            clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
            timer = null;        //此处一点要清空 
          }, delay); 
        };
      }

虽然网上这些防抖节流函数有很多版本,重要的知道为什么这么写,能够实现功能就可以了,没必要搞的那么复杂;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【【51单片机的I2C总线】】

51单片机的I2C总线 学会总线&#xff0c;掌控芯片。了解串口&#xff0c;真理全有。 I&#xff12;C时序 &#xff11;.起始条件&#xff1a;  SCL在高电平期间&#xff0c;SDA从高电平切换到低电平 终止条件&#xff1a; SCL在高电平期间&#xff0c;SDA从低电平切换为高电…

【go语言学习笔记】01 Go语言快速入门

文章目录 一、基础入门1. 示例程序2. 安装与环境变量设置3. 项目构建和编译发布3.1 go build和go install对比3.2 跨平台编译 二、数据类型1. 基础类型1.1 整型1.2 浮点数1.3 布尔型1.4 字符串1.5 零值 2. 变量2.1 变量声明2.1.1 指定变量类型2.1.2 根据值自行判定变量类型2.1.…

深入学习 Redis - 深挖经典数据类型之 list

目录 前言 一、list 类型 1.1、操作命令 lpush / rpush&#xff08;插入元素&#xff09; lrange&#xff08;查看范围元素&#xff09; lpushx / rpushx &#xff08;有约束的插入&#xff09; lpop / rpop&#xff08;头删尾删&#xff09; lindex&#xff08;获取下…

2023年第三届能源、电力与电气工程国际会议 (CoEEPE 2023)

会议简介 Brief Introduction 2023年第三届能源、电力与电气工程国际会议(CoEEPE 2023) 会议时间&#xff1a;2023年11月22日-24日 召开地点&#xff1a;澳大利亚墨尔本 大会官网&#xff1a;www.coeepe.org 2023年第三届能源、电力与电气工程国际会议(CoEEPE 2023)由安徽大学、…

使用GGML和LangChain在CPU上运行量化的llama2

Meta AI 在本周二发布了最新一代开源大模型 Llama 2。对比于今年 2 月发布的 Llama 1&#xff0c;训练所用的 token 翻了一倍&#xff0c;已经达到了 2 万亿&#xff0c;对于使用大模型最重要的上下文长度限制&#xff0c;Llama 2 也翻了一倍。 在本文&#xff0c;我们将紧跟趋…

JavaScript基础语法及小案例

目录 JavaScript基础语法1. 变量声明和赋值2. 数据类型1) 基本数据类型2) 复合数据类型(引用类型)3) 特殊数据类型 3. 运算符1) 算术运算符2) 赋值运算符3) 比较运算符4) 逻辑运算符5) 三元运算符 4. 控制流程1) 条件语句2) 循环语句 5. 函数1) 函数的基本使用① 什么是函数② …

DXFReader.NET 2023 Crack

DXFReader.NET 是一个 .NET 组件&#xff0c;允许直接从 AutoCAD 图形文件格式 DXF&#xff08;也称为图形交换格式&#xff09;查看、操作和打印。 DXFReader.NET 之 DXF 是 Drawing eXchange Format 的首字母缩写。DXF 是图形文件内容的复制&#xff0c;支持将文件从一个 CA…

机器学习深度学习——预备知识(上)

深大的夏令营已经结束&#xff0c;筛选入营的保研er就筛选了1/3&#xff0c;280多的入营总人数里面双非只有30左右。 最终虽然凭借机试拿到offer了&#xff0c;但是我感受到了自己的明显短板&#xff0c;比如夏令营的舍友就都有一篇核心论文&#xff0c;甚至还有SCI一区一作的。…

Mac应用程序因“来自身份不明的开发者”无法打开如何解决

相信不少mac电脑用户在安装应用程序时经常会遇到“xxx.app已损坏&#xff0c;打不开。这是mac系统的新的安全机制&#xff0c;安装 App 时提示&#xff1a; 常见的几种报错提示 xxx 已损坏&#xff0c;无法打开。您应该将它移到废纸篓打不开 xxx&#xff0c;因为它来自身份不明…

旋翼式水表安装注意事项

旋翼式水表是一种常用的水流计量设备&#xff0c;适用于小口径管道的单向水流总量的计量。如果你正在考虑安装旋翼式水表&#xff0c;以下是一些需要注意的事项&#xff1a; 1.安装位置的选择&#xff1a;旋翼式水表应该安装在管道的垂直方向上&#xff0c;并且水流方向必须与水…

探秘ArrayList源码:Java动态数组的背后实现

探秘ArrayList源码&#xff1a;Java动态数组的背后实现 一、成员变量二、构造器1、默认构造器2、带初始容量参数构造器3、指定collection元素参数构造器 三、add()方法扩容机制四、场景分析1、对于ensureExplicitCapacity&#xff08;&#xff09;方法1.1 add 进第 1 个元素到 …

MQTT的理解和使用

MQTT是一种基于发布/订阅模式的轻量协议&#xff0c;该协议基于TCP/IP协议上&#xff0c;由IBM在1999年发布。 流程理解&#xff1a;订阅者在订阅时会选择主题&#xff08;Topic&#xff09;和服务质量&#xff08;QoS&#xff09;&#xff0c;然后发布者发布消息&#xff0c…

matlab超前-滞后校正

1控制系统的校正 系统性能 稳定性、准确性、快速性 动态性能-超前校正 阶跃曲线、频域&#xff08;bode图&#xff09;、根轨迹&#xff08;增加零点-根轨迹左移稳定性提高&#xff09;、PID控制&#xff08;PD&#xff09; 静态性能-滞后校正 阶跃曲线、频域&#xff08…

Flink CDC MongoDB 联合实时数仓的探索实践

摘要&#xff1a;本文整理自 XTransfer 技术专家, Flink CDC Maintainer 孙家宝&#xff0c;在 Flink Forward Asia 2022 数据集成专场的分享。本篇内容主要分为四个部分&#xff1a; MongoDB 在实时数仓的探索 MongoDB CDC Connector 的实现原理和使用实践 FLIP-262 MongoDB…

Spring MVC拦截器和跨域请求

一、拦截器简介 SpringMVC的拦截器&#xff08;Interceptor&#xff09;也是AOP思想的一种实现方式。它与Servlet的过滤器&#xff08;Filter&#xff09;功能类似&#xff0c;主要用于拦截用户的请求并做相应的处理&#xff0c;通常应用在权限验证、记录请求信息的日志、判断用…

多肽试剂1801415-23-5,Satoreotide,UNII-S58172SSTS,应用在多肽标记及修饰上

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ Satoreotide&#xff0c;UNII-S58172SSTS Product structure Product specifications 1.CAS No&#xff1a;1801415-23-5 2.Molecular formula&#xff1a;C58H72ClN15O14S2 3.Molecular weight&#xff1a;1302.9 4.Packa…

【C++详解】——C++11

目录 C简介 统一的列表初始化 {}的初始化 initializer_list容器 声明 auto decltype nullptr 范围for C简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了 C98称为C11之前的最新C标准名称。 不过由于C03(TC1)主…

STM32 串口 DMA 接收任意长度数据

DMA 局限性 DMA 传输完成会产生中断告知 CPU&#xff0c;这对于固定长度的数据是没什么问题的。但是对于不定长的数据就不行了&#xff0c;DMA 一定要接收到足够多&#xff08;设定的长度&#xff09;的数据时才产生完成中断&#xff0c;如果接收到的数据量小于设定的长度&…

Linux的基础配置

配置篇 前置步骤&#xff1a;先租一个服务器或装个Linux再或者虚拟机都可以 1.安装gcc,g,gdb 在Linux下我们能用什么工具来编译所编写好的代码呢&#xff0c;其实Linux下这样的工具有很多&#xff0c;但我们只介绍两款常用的工具&#xff0c;它们分别是gcc和g. gcc和g的主要…

解决端口占用

解决办法&#xff1a; 1、换一个其它未被占用的端口 2、端口被占用了&#xff0c;先看下是哪个程序再用&#xff0c;停掉就OK了 下面演示结束端口被占用的程序的过程&#xff1a; 1、查看被占用的端口的进程 netstat -aon|findstr 端口号2、根据PID找到占用此端口的进程 ta…