JavaScript 防抖与节流

news2024/10/7 16:15:23

目录

  • 1 函数
    • 1.1 调用函数
    • 1.2 闭包
  • 2 防抖与节流
    • 2.1 定义
    • 2.2 区别
    • 2.3 应用场景
  • 3 防抖
    • 3.1 非立即执行
      • 3.1.1 一般写法
      • 3.1.2 Vue2 中写法
      • 3.1.3 过程
    • 3.2 立即执行
      • 3.2.1 一般写法
      • 3.2.2 Vue2 中写法
      • 3.2.3 过程

1 函数

应用防抖节流首先需理解以下知识

1.1 调用函数

js 函数内部 return 一个函数,自动调用 toString 方法

  1. 调用函数加括号 fn():执行函数体 fn,执行后得到其返回值
  2. 调用函数不加括号 fn:不会执行函数体,而是得到函数体的源码。
    • 函数名其实就是指向函数的指针,它只是传递了函数体所在的地址位置,在需要执行时找到函数体去执行。

1.2 闭包

JS 中 return 一个函数与直接 return 一个函数变量的区别
函数的节流与防抖

function makeCounter() {
  var count = 0;
  function counter() {
    count = count + 1;
    return count;
  }
  return counter(); // 将嵌套函数返回
}
var doCount = makeCounter();
console.log(doCount, "--doCount1"); // 1 '--doCount1'
console.log(doCount, "--doCount2"); // 1 '--doCount2'
console.log(doCount, "--doCount3"); // 1 '--doCount3'
  • 当 return counter()时,就自动调用了嵌套函数。
  • 那么嵌套函数返回一个经过+1 的 count,并且 count 的值为 1.
  • 所以 doCount 得到的是一个数字,并不是函数,所以无法得到闭包。
function makeCounter() {
  var count = 0;
  function counter() {
    count = count + 1;
    return count;
  }
  return counter; // 将嵌套函数返回,但只写函数名称
}
var doCount = makeCounter();
console.log(doCount(), "--doCount1"); // 1 '--doCount1'
console.log(doCount(), "--doCount2"); // 2 '--doCount2'
console.log(doCount(), "--doCount3"); // 3 '--doCount3'
  • return counter 返回的是整一个 cunter()函数。

  • 因此执行 var doCount = makeCounter()时,doCount 将引用 counter 函数及其中的变量环境。

  • 那么 counter 函数及其中的变量环境,就是闭包了

  • 闭包的形成:内部函数引用了外部函数的数据(这里为 count),

  • 因此在 doCount=makeCounter(),则会把这个 count 保存在 doCount 中,函数执行完,count 并不会被销毁。

  • 注意: 为什么上面这段代码没有直接写的 function doCount(){…} 而是把 function 赋值给了变量 doCount 呢?

  • 我们通常会想当然的认为每次调用 doCount() 都会重走一遍 doCount()中的代码块, 但其实不然。

  • 注意 makeCounter 方法中的 return 不是 1,2,3 这样的数值, 而是一个方法,并且把这个方法赋值给了 doCount 变量。

  • 那么在这个 makeCounter 自运行一遍之后, 其实最后赋值给 doCount 的是 count = count + 1; return count; 这段代码。

  • 所以后面每次调用 doCount() 其实都是在调用 count = count + 1; return count;

  • 闭包会持有父方法的局部变量并且不会随父方法销毁而销毁, 所以这个 count 其实就是来自于第一次 makeCounter 执行时创建的变量

2 防抖与节流

什么是防抖和节流?有什么区别?如何实现?
防抖节流详细用法和区别 - 详解版

2.1 定义

  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

  • 一个经典的比喻:

    • 想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
    • 假设电梯有两种运行策略 debounce 和 throttle,超时设定为 15 秒,不考虑容量限制
    • 电梯第一个人进来后,15 秒后准时运送一次,这是节流
    • 电梯第一个人进来后,等待 15 秒。如果过程中又有人进来,15 秒等待重新计时,直到 15 秒后开始运送,这是防抖

2.2 区别

  • 防抖是将多次执行变成最后一次执行;
  • 节流是将多次执行变为每隔一段时间执行一次。
正常执行runrunrunrunrunrunrunrun
防抖: 非立即执行run
防抖: 立即执行run

在这里插入图片描述
在这里插入图片描述

2.3 应用场景

  • 防抖在连续的事件,只需触发一次回调的场景有:
    • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
    • 手机号、邮箱验证输入检测
    • 窗口大小 resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
    • 登录、
    • 点击按钮提交表单、
    • 点赞、收藏、标心…
  • 节流在间隔一段时间执行一次回调的场景有:
    • 滚动加载,加载更多或滚到底部监听
    • 搜索框,搜索联想功能,隔一段时间就请求
    • scroll 滑动事件、
    • resize 浏览器窗口改变事件、
    • mousemove 鼠标移动事件、
    • 文档编辑隔一段时间自动保存…

3 防抖

防抖有两种实现方式:非立即执行版 和 立即执行版。

  • 非立即执行版:事件触发 -> 延时 -> 执行回调函数。
    • 如果延时过程中事件再次被触发,则请控制之前计时器重新计时。没有触发则等延迟结束后,执行回调函数。
  • 立即执行版:事件触发 -> 立即执行 -> 延时。
    • 延时过程中事件被触发则继续执行延时,延时结束后不会执行回调函数(比如表单提交,点赞,收藏)。

3.1 非立即执行

3.1.1 一般写法

Function.prototype.apply()

// 1
function debounce(func, delay) {
  let timeout;
  return function () {
    let context = this;
    let args = arguments;

    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}
// 2
function debounce(func, delay) {
  let timeout;
  return () => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(func, delay);
  };
}
// 使用
debounce(() => {
  console.log("run");
}, 500);

3.1.2 Vue2 中写法

【记】Vue 中使用防抖函数所遇见的坑

Vue 中使用时,需要定义 timeout,同时在防抖函数中,this 的指向发生了变化,需要在 return 之前获取 vue 实例。
这个时候,你直接使用,还是不行的,只要 debug 就会发现 debounce 返回的 func 没有进去,需要手动执行一下(添加括号)。

要把 timeout 挂在 this 上,否则不起作用

data() {
  return {
    timeout: null // 关键在这里
  }
}
// 1
methods: {
  debounce(func, delay) {
    let context = this; // this指向发生变化,需要提出来
    let args = arguments;
    return (function () {
      if (context.timeout) {
        clearTimeout(context.timeout);
      }

      context.timeout = setTimeout(() => {
        func.apply(context, args);
      }, delay);
    })(); // 注意这里有()
  },
}
// 2
methods: {
  debounce(func, delay) {
    let context = this; // this指向发生变化,需要提出来
    return (function () {
      if (context.timeout) {
        clearTimeout(context.timeout);
      }

      context.timeout = setTimeout(() => {
        func();
        context.timeout = null; // 必须要清空,否则影响另一事件
      }, delay);
    })(); // 注意这里有()
  },
}
// 使用
debounce(() => {
  console.log("run");
}, 500);

若要封装成公共方法,把 context 作为参数

export const debounce = (func, context, delay = 500) => {
  return (function () {
    if (context.timeout) {
      clearTimeout(context.timeout);
    } else {
      func();
    }
    context.timeout = setTimeout(() => {
      context.timeout = null;
    }, delay);
  })();
};
// 选择式 API
// 使用
debounce(
  () => {
    console.log("run");
  },
  this,
  500
);

3.1.3 过程

var count = 1;

// 非立即执行
export const debounce = (func, context, delay = 500) => {
  console.log(count, "--count--1");

  return (function () {
    console.log(context.timeout, "--context.timeout--1");

    if (context.timeout) {
      clearTimeout(context.timeout);
      console.log(context.timeout, "--context.timeout--2");
    }
    context.timeout = setTimeout(func, delay);
    console.log(context.timeout, "--context.timeout--3");

    count += 1;
    console.log(count, "--count--2");
  })();
};

连续点击 3 次

1 '--count--1'
null '--context.timeout--1'
110 '--context.timeout--3'
2 '--count--2'
2 '--count--1'
110 '--context.timeout--1'
110 '--context.timeout--2'
116 '--context.timeout--3'
3 '--count--2'
3 '--count--1'
116 '--context.timeout--1'
116 '--context.timeout--2'
122 '--context.timeout--3'
4 '--count--2'
--ok

前两次 func 不执行,因为被 clearTimeout 了

3.2 立即执行

3.2.1 一般写法

// 1
function debounce(func, delay) {
  let timeout;
  return function () {
    let context = this;
    let args = arguments;

    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}
// 2
function debounce(func, delay) {
  let timeout;
  return () => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(func, delay);
  };
}
// 使用
debounce(() => {
  console.log("run");
}, 500);

3.2.2 Vue2 中写法

data() {
  return {
    timeout: null
  }
}
// 1
methods: {
  debounce(func, delay) {
    let context = this; // this指向发生变化,需要提出来
    let args = arguments;
    return (function () {
      if (context.timeout) {
        clearTimeout(context.timeout);
      } else {
        func.apply(context, args);
      }
      context.timeout = setTimeout(() => {
        context.timeout = null;
      }, delay);
    })(); // 注意这里有()
  },
}
// 2
methods: {
  debounce(func, delay) {
    let context = this; // this指向发生变化,需要提出来
    return (function () {
      if (context.timeout) {
        clearTimeout(context.timeout);
      } else {
        func();
      }
      context.timeout = setTimeout(() => {
        context.timeout = null;
      }, delay);
    })(); // 注意这里有()
  },
}
// 使用
debounce(() => {
  console.log("run");
}, 500);

若要封装成公共方法,把 context 作为参数

export const debounce = (func, context, delay = 500) => {
  return (function () {
    if (context.timeout) {
      clearTimeout(context.timeout);
    } else {
      func();
    }
    context.timeout = setTimeout(() => {
      context.timeout = null;
    }, delay);
  })();
};
// 选择式 API
// 使用
debounce(
  () => {
    console.log("run");
  },
  this,
  500
);

3.2.3 过程

var count = 1;

export const debounce = (func, context, delay = 500) => {
  console.log(count, "--count--1");
  return (function () {
    console.log(context.timeout, "--context.timeout--1");

    if (context.timeout) {
      clearTimeout(context.timeout);
      console.log(context.timeout, "--context.timeout--2");
    } else {
      func();
      console.log("--func");
    }
    context.timeout = setTimeout(() => {
      context.timeout = null;
      console.log(context.timeout, "--context.timeout--4");
    }, delay);
    console.log(context.timeout, "--context.timeout--3");

    count += 1;
    console.log(count, "--count--2");
  })();
};

延时器方法 setTimeout () 的返回值是一个代表定时器唯一身份标识的编号

连续点击 4 次结果

1 '--count--1'
null '--context.timeout--1'
--func
126 '--context.timeout--3'
2 '--count--2'
2 '--count--1'
126 '--context.timeout--1'
126 '--context.timeout--2'
132 '--context.timeout--3'
3 '--count--2'
3 '--count--1'
132 '--context.timeout--1'
132 '--context.timeout--2'
138 '--context.timeout--3'
4 '--count--2'
4 '--count--1'
138 '--context.timeout--1'
138 '--context.timeout--2'
138 '--context.timeout--3'
5 '--count--2'
null '--context.timeout--4'
  • 最后才输出 null '--context.timeout--4' 因为前面的几个定时器 都被 clearTimeout 了,不会执行

JavaScript 防抖 (vue中写法总结)

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

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

相关文章

电影售票系统

项目介绍 基于SpringBoot ,Mybatis, Vue 的电影售票及影院管理系统(前后端分离),具体功能见 下面演示截图 需要安装的软件 Java8 MySQL5.7或以上 Navicat或者其他管理工具 IDEA或者Eclipse Node.js 14或以上 运行项…

PLC学习笔记(三):PLC结构(2)

目录: PLC学习笔记(一):概述 PLC学习笔记(二):PLC结构(1) PLC学习笔记(三):PLC结构(2) 🦁&…

SpringBoot+Mybaits搭建通用管理系统实例八:系统权限控制实现

一、本章内容 实现自定义权限控制,通过自定义PermissionEvaluator实现操作权限的检测及控制,关于权限控制模型有ACL, DAC, MAC, RBAC, ABAC等,具体原理可参考:【权限系统设计】ACL, DAC, MAC, RBAC, ABAC 模型的不同应用场景 完整课程地址 二、开发视频 SpringBoot+Mybaits…

《操作系统-真象还原》12. 进一步完善内核

文章目录Linux 的系统调用系统调用的实现 —— 图解系统调用的实现 —— 代码触发中断寻找 IDT 中断描述符执行对应的中断例程中断例程中通过用户传入的功能号去执行对应的功能函数关于 printf你需要知道可变参数的原理Linux 中的可变参数原理Linux 中的可变参数实现printf 只是…

【微服务】SpringCloud轮询拉取注册表及服务发现源码解析

💖 Spring家族及微服务系列文章 ✨【微服务】SpringCloud微服务剔除下线源码解析 ✨【微服务】SpringCloud微服务续约源码解析 ✨【微服务】SpringCloud微服务注册源码解析 ✨【微服务】Nacos2.x服务发现?RPC调用?重试机制? ✨【微…

机器学习:支持向量机SVM的SVC和SVR

支持向量机SVMSVM的工作原理及分类支持向量机的原理线性可分的SVM非线性可分的支持向量机支持向量机分类SVC支持向量机回归SVRSVR原理SVR模型时间序列曲线预测SVM的工作原理及分类 支持向量机的原理 支持向量机(Support Vector Machine,SVM)是一种二类分类器&…

积极融入信创生态 | 思腾合力软件产品完成多个信创产品适配

从2019年我国提出发展信创产业,2020年迈入信创发展元年,到2022信创开始向行业深水区迈进,逐渐延伸到金融、电信等重点行业、核心业务中,开启了“行业信创元年”。一个真正的“大信创”时代已开启,一个数万亿规模的市场…

MybatisPlus---从入门到深化

目录 MyBatisPlus入门 MyBatisPlus介绍 ​编辑Spring集成MyBatisPlus SpringBoot集成MyBatisPlus MyBatisPlus_CRUD 添加 CRUD_相关注解 修改 删除 查询 条件构造器 全局配置 ActiveRecord_概念 ActiveRecord_增删改查 MyBatisPlus插件_插件概述 MyBatisPlus插件_…

超神之路 数据结构 3 —— Stack栈实现及应用

栈也是一种线性表结构,相较于数组,栈对应的操作是数组的子集,我们只要实现从一端添加元素,并从这个一端取出元素,这一端我们称呼它为栈顶,正是由于这种结构,它具有“后入先出”(LIFO…

PTA题目 计算工资

某公司员工的工资计算方法如下:一周内工作时间不超过40小时,按正常工作时间计酬;超出40小时的工作时间部分,按正常工作时间报酬的1.5倍计酬。员工按进公司时间分为新职工和老职工,进公司不少于5年的员工为老职工&#…

基于jsp+mysql+ssm健身信息交流网站-计算机毕业设计

项目介绍 随着全民健身运动的兴起,越来越多的人走进了健身房,而传统的管理模式已不能适应现代健身机构的发展趋势,如何增强健身房会员卡的管理和完善客户服务,成了健身房发展的当务之急。健身信息管理系统的研究与开发&#xff0…

文本摘要实战:基于句子相似度矩阵构建图结构实现文本摘要 代码+数据

任务描述: 自动文本摘要(Text Summarization)是指给出一段文本,我们从中提取出要点,然后再形成一个短的概括性的文本。自动的文本摘要是非常具有挑战性的,当我们作为人类总结一篇文章时,我们通常会完整地阅读它以发展我们的理解,然后写一个摘要突出其要点。由于计算机缺乏…

计算机毕业设计springboot+vue基本微信小程序的码高教育课后在线小程序

项目介绍 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,码高教育课后在线小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行码高教育课后在线小程序的设计与开…

HTML期末大学生网页设计作业----锤子手机 1页

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材,DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 在线商城购物 | 水果商城 | 商城系统建设 | 多平台移动商城 | H5微商城购物商城项目 | HTML期末大学生网页设计作业,Web大学生网页 HTML&a…

java计算机毕业设计ssm基于JAVA的网上购物系统-商城购物网站

项目介绍 本网上购物网站是针对目前商城的实际需求,从实际工作出发,对过去的网上购物存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息,取代人工管理模式,查询便利,信息准确率高,节省了开支,提高了工作的效率。 本系统结合计算机系统的结构、概念、模型、原理…

【机器学习】红酒数据集和加利福尼亚的房价数据的随机森林算法详解

一.随机森林 1.1随机森林的构建 bootstrap参数代表的是bootstrap sample,也就是“有放回抽样”的意思,指每次从样本空间中可以重复抽取同一个样本(因为样本在第一次被抽取之后又被放回去了) 假设,原始样本是”苹果&…

【POJ No. 3253】 围栏修复 Fence Repair

【POJ No. 3253】 围栏修复 Fence Repair 北大OJ 题目地址 这道题其实我们 之前就做过了 https://blog.csdn.net/weixin_44226181/article/details/127064923 当时我们 是在学习哈夫曼树 【题意】 约翰想修牧场周围的篱笆,需要N 块(1≤N ≤20000&…

CMake中if的使用

CMake中的if命令用于有条件地执行一组命令&#xff0c;其格式如下&#xff1a; if(<condition>)<commands> elseif(<condition>) # optional block, can be repeated<commands> else() # optional block<commands> endif() 根据Co…

HTML5期末大作业:旅游网页设计与实现——旅游风景区网站HTML+CSS (1)

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 游景点介绍 | 旅游风景区 | 家乡介绍 | 等网站的设计与制作 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&…

【项目实战:核酸检测平台】第一章 逆向工程

引言 准备做个项目实战系列&#xff0c;选一些比较有代表性&#xff0c;难度适中、规模合适的项目&#xff0c;从不同的角度&#xff0c;不同的深度去解析。很多初学者都抱怨没有实战项目&#xff0c;而学校/机构里大量用烂的学生管理、图书管理、电子商城等项目实在没有什么新…