当前位置: 首页 > article >正文

手写节流防抖函数

1. 认识防抖和节流函数

防抖和节流的概念最早不是出现在软件工程中,防抖是出现在电子元件中,节流是出现的流体流动中。

  • 而javascript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理
  • 而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生

1.1. 认识防抖debounce函数

场景:在实际开发中,常常会碰到点击一个按钮请求网络接口的场景,这时用户如果因为手抖多点了几下按钮,就会出现短时间内多次请求接口的情况,实际上这会造成性能的消耗,我们其实只需要监听最后一次的按钮,但是我们并不知道哪一次会是最后一次,就需要做个延时触发的操作,比如这次点击之后的300毫秒内没再点击就视为最后一次。这就是防抖函数使用的场景

总结防抖函数的逻辑

  • 当事件触发时,相应的函数并不会立即触发,而是等待一定的时间;
  • 当事件密集触发时,函数的触发会被频繁的推迟;
  • 只有等待了一段时间也没事件触发,才会真正的响应函数

1.2 认识节流throttle函数

场景:开发中我们会有这样的需求,在鼠标移动的时候做一些监听的逻辑比如发送网络请求,但是我们知道document.onmousemove监听鼠标移动事件触发频率是很高的,我们希望按照一定的频率触发,比如3秒请求一次。不管中间document.onmousemove监听到多少次只执行一次。这就是节流函数的使用场景

总结节流函数的逻辑

  • 当事件触发时,会执行这个事件的响应函数;
  • 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行;
  • 不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;

2. 实现防抖函数

2.1 基本实现v-1

const debounceElement = document.getElementById("debounce");const handleClick = function (e) {console.log("点击了一次");
};// debounce防抖函数
function debounce(fn, delay) {// 定一个定时器对象,保存上一次的定时器let timer = null// 真正执行的函数function _debounce() {// 取消上一次的定时器if (timer) {clearTimeout(timer);}// 延迟执行timer = setTimeout(() => {fn()}, delay);}return _debounce;
}debounceElement.onclick = debounce(handleClick, 300);

参考 前端手写面试题详细解答

2.2 this-参数v-2

上面handleClick函数有两个问题,一个是this指向的是window,但其实应该指向debounceElement,还一个是无法传递传递参数。

优化:

const debounceElement = document.getElementById("debounce");const handleClick = function (e) {console.log("点击了一次", e, this);
};function debounce(fn, delay) {let timer = null;function _debounce(...args) {if (timer) {clearTimeout(timer);}timer = setTimeout(() => {fn.apply(this, args) // 改变this指向 传递参数}, delay);}return _debounce;
}debounceElement.onclick = debounce(handleClick, 300);

2.3 可选是否立即执行v-3

有些时候我们想点击按钮的第一次就立即执行,该怎么做呢?

优化:

const debounceElement = document.getElementById("debounce");const handleClick = function (e) {console.log("点击了一次", e, this);
};// 添加一个immediate参数 选择是否立即调用
function debounce(fn, delay, immediate = false) {let timer = null;let isInvoke = false; // 是否调用过function _debounce(...args) {if (timer) {clearTimeout(timer);}// 如果是第一次调用 立即执行if (immediate && !isInvoke) {fn.apply(this.args);isInvoke = true;} else {// 如果不是第一次调用 延迟执行 执行完重置isInvoketimer = setTimeout(() => {fn.apply(this, args);isInvoke = false;}, delay);}}return _debounce;
}debounceElement.onclick = debounce(handleClick, 300, true);

2.4 取消功能v-4

有些时候我们设置延迟时间很长,在这段时间内想取消之前点击按钮的事件该怎么做呢?

优化:

const debounceElement = document.getElementById("debounce");
const cancelElemetnt = document.getElementById("cancel");const handleClick = function (e) {console.log("点击了一次", e, this);
};function debounce(fn, delay, immediate = false) {let timer = null;let isInvoke = false; function _debounce(...args) {if (timer) {clearTimeout(timer);}if (immediate && !isInvoke) {fn.apply(this.args);isInvoke = true;} else {timer = setTimeout(() => {fn.apply(this, args);isInvoke = false;}, delay);}}// 在_debounce新增一个cancel方法 用来取消定时器_debounce.cancel = function () {clearTimeout(timer);timer = null;};return _debounce;
}const debonceClick = debounce(handleClick, 5000, false);
debounceElement.onclick = debonceClick;
cancelElemetnt.onclick = function () {console.log("取消了事件");debonceClick.cancel();
};

2.5 返回值v-5(最终版本)

最后一个问题,上面handleClick如果有返回值我们应该怎么接收到呢

优化:用Promise回调

const debounceElement = document.getElementById("debounce");
const cancelElemetnt = document.getElementById("cancel");const handleClick = function (e) {console.log("点击了一次", e, this);return "handleClick返回值";
};function debounce(fn, delay, immediate = false) {let timer = null;let isInvoke = false;function _debounce(...args) {return new Promise((resolve, reject) => {if (timer) clearTimeout(timer);if (immediate && !isInvoke) {try {const result = fn.apply(this, args);isInvoke = true;resolve(result); // 正确的回调} catch (err) {reject(err); // 错误的回调}} else {timer = setTimeout(() => {try {const result = fn.apply(this, args); isInvoke = false;resolve(result); // 正确的回调} catch (err) {reject(err); // 错误的回调}}, delay);}});}_debounce.cancel = function () {clearTimeout(timer);timer = null;};return _debounce;
}const debonceClick = debounce(handleClick, 300, true);
// 创建一个debonceCallBack用于测试返回的值
const debonceCallBack = function (...args) {debonceClick.apply(this, args).then((res) => {console.log({ res });});
};debounceElement.onclick = debonceCallBack;
cancelElemetnt.onclick = () => {console.log("取消了事件");debonceClick.cancel();
};

3. 实现节流函数

3.1 基本实现v-1

这里说一下最主要的逻辑,只要 这次监听鼠标移动事件处触发的时间减去上次触发的时间大于我们设置的间隔就执行想要执行的操作就行了

nowTime−lastTime>interval

nowTime:这次监听鼠标移动事件处触发的时间

lastTime:监听鼠标移动事件处触发的时间

interval:我们设置的间隔

const handleMove = () => {console.log("监听了一次鼠标移动事件");
};const throttle = function (fn, interval) {// 记录当前事件触发的时间let nowTime;// 记录上次触发的时间let lastTime = 0;// 事件触发时,真正执行的函数function _throttle() {// 获取当前触发的时间nowTime = new Date().getTime();// 当前触发时间减去上次触发时间大于设定间隔if (nowTime - lastTime > interval) {fn();lastTime = nowTime;}}return _throttle;
};document.onmousemove = throttle(handleMove, 1000);

3.2 this-参数v-2

和防抖一样,上面的代码也会有this指向问题 以及 参数传递

优化:

const handleMove = (e) => {console.log("监听了一次鼠标移动事件", e, this);
};const throttle = function (fn, interval) {let nowTime;let lastTime = 0;function _throttle(...args) {nowTime = new Date().getTime();if (nowTime - lastTime > interval) {fn.apply(this, args);lastTime = nowTime;}}return _throttle;
};document.onmousemove = throttle(handleMove, 1000);

3.3 可选是否立即执行v-3

上面的函数第一次默认是立即触发的,如果我们想自己设定第一次是否立即触发该怎么做呢?

优化:

const handleMove = (e) => {console.log("监听了一次鼠标移动事件", e, this);
};const throttle = function (fn, interval, leading = true) {let nowTime;let lastTime = 0;function _throttle(...args) {nowTime = new Date().getTime();// leading为flase表示不希望立即执行函数 // lastTime为0表示函数没执行过if (!leading && lastTime === 0) {lastTime = nowTime;}if (nowTime - lastTime > interval) {fn.apply(this, args);lastTime = nowTime;}}return _throttle;
};document.onmousemove = throttle(handleMove, 3000, false);

3.4 可选最后一次是否执行v-4(最终版本)

如果最后一次监听的移动事件与上一次执行的时间不到设定的时间间隔,函数是不会执行的,但是有时我们希望无论到没到设定的时间间隔都能执行函数,该怎么做呢?

我们的逻辑是:因为我们不知道哪一次会是最后一次,所以每次都设置一个定时器,定时器的时间间隔是距离下一次执行函数的时间;然后在每次进来都清除上次的定时器。这样就能保证如果这一次是最后一次,那么等到下一次执行函数的时候就必定会执行最后一次设定的定时器。

const handleMove = (e) => {console.log("监听了一次鼠标移动事件", e, this);
};// trailing用来选择最后一次是否执行
const throttle = function (fn,interval,leading = true,trailing = false) {let nowTime;let lastTime = 0;let timer;function _throttle(...args) {nowTime = new Date().getTime();// leading为flase表示不希望立即执行函数// lastTime为0表示函数没执行过if (!leading && lastTime === 0) {lastTime = nowTime;}if (timer) {clearTimeout(timer);timer = null;}if (nowTime - lastTime >= interval) {fn.apply(this, args);lastTime = nowTime;return;}// 如果选择了最后一次执行 就设置一个定时器if (trailing && !timer) {timer = setTimeout(() => {fn.apply(this, args);timer = null;lastTime = 0;}, interval - (nowTime - lastTime));}}return _throttle;
};document.onmousemove = throttle(handleMove, 3000, true, true);

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

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

相关文章:

手写节流防抖函数

1. 认识防抖和节流函数 防抖和节流的概念最早不是出现在软件工程中,防抖是出现在电子元件中,节流是出现的流体流动中。 而javascript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理而对于某些频繁的事件处理会造成性能…...

camunda_11_connector

Camunda 的 service task 推荐使用 external task, 它有很多优点: 流程引擎可以做到轻量级, 流程引擎实例可以支持更多的业务.解耦流程引擎和业务代码, 以后的升级和部署将非常方便.借助external task SDK, 业务代码实现也非常简单external task 采用 pull 模式, 由 external t…...

通达信自动交易接口设置止损程序解析

通达信自动交易接口设置止损程序并不是很难,对于交易者来说,还是需要去学习一些编程知识,像交易中的止损程序,可以这样去编写和输入你的止损策略: (1)# 设置买卖止损值 def set_stop_lose_n…...

MySQL事务的理解

什么是事务 事务是是数据库操作的最小的单元,它包含了一个或者多个操作命令,这些命令作为一个整体来执 行,要么一起成功要么一起失败,事务是不可在分的一个整体的操作集合。 事务具备的四大特性 原子性:事务是一个…...

ubantu服务器崩溃,重装系统如何使用之前的账号

1.进入root账户下: sudo su 2.查看账号拥有者和所属组 ls -la 2.给现在系统,添加原来相同的已存在账号名: adduser newusername 注意:报告已存在用户名称!不用管,这个错误。已经添加到新系统中了。 3.修…...

Python 逻辑回归

逻辑回归分类 训练二元分类器 加载仅有两个分类的数据 from sklearn.linear_model import LogisticRegression from sklearn import datasets from sklearn.preprocessing import StandardScaleriris datasets.load_iris() features iris.data[:100,:] target iris.target…...

web前端面试题附答案016-怎么让顶部轮播图渲染的更快?

一、为什么强调轮播图? 很多时候我们强调用户体验,而这里更多时候我们更强调完美的首屏体验,而现在几乎每个网站顶部第一个大模块就是轮播图。轮播图占得区域最大,图片质量也更高,几乎一张图片的面积,体积就…...

Python脚本,物联网云服务器端口监控

事实上,物联网的思路很简单,客户端设备通过TCP协议上传到某个云服务器的端口,我们需要在这个云服务器上编写一个小小的脚本去创建某个端口,持续监听,可以互相发送数据,这个脚本语言可以是JAVA,也…...

Python之wxPython框架的使用

Python之wxPython框架的使用一、安装wxPython二、创建一个 wx.App 的子类三、直接使用wx.App四、使用wx.Frame 框架五、常用控件1.Static Text 文本类2.TextCtrl 输入文本类3.Button 按钮类一、安装wxPython wxPython是个成熟而且特性丰富的跨平台GUI工具包。由Robin Dunn 和Ha…...

量子密钥分发B92协议——笔记

一、B92介绍 (参照邓富国的博士论文) 1、什么是量子密钥分配(QKD) 通信双方以量子态为信息载体,利用量子力学原理,通过量子信道传送,在彼此之间建立共享密钥的方法。 2、B92协议简介&#x…...

【教程】微服务使用http客户端Feign

【教程】http客户端Feign RestTemplate 方式调用存在的问题 先来看我们以前利用RestTemplate发起远程调用的代码 String url = "http://userservice/user/" + order.getUserId(); User user = restTemplate.getForObject(url,User.class...

Python3 正则表达式

在 Python3 中正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。 Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。 re 模块使 Python 语言拥有全部的正则表达式功能。 compile 函数根据一个模…...

分享114个JS特效动画效果,总有一款适合您

分享114个JS特效动画效果,总有一款适合您 114个JS特效动画效果下载链接:https://pan.baidu.com/s/18_NR3eaxDddWSHqAbMYiuw?pwdm25p 提取码:m25p Python采集代码下载链接:https://wwgn.lanzoul.com/iKGwb0kye3wj jQuery动画…...

Canadian Coding Competition(CCC) 2020 Senior 题解

Problem S1: Surmising a Sprinter’s Speed Problem S1: 推测短跑运动员的速度 Problem Description Trick E. Dingo is trying, as usual, to catch his nemesis the Street Sprinter. His past attempts using magnets, traps and explosives have failed miserably, s…...

40 计算机组成原理12h-北大陆俊林老师

P11 - 1 - 101-电子计算机的兴起(16-50--)16:51P21 - 2 - 102-冯诺依曼结构的要点(13-59--)14:00P31 - 3 - 103-冯诺依曼结构的小故事(9-22--)09:23P41 - 4 - 104-计算机结构的简化模型(10-28--…...

【Note4】rsyslog,logrotate,journalctl

文章目录1.rsyslog:rsyslogd一个进程 ,管理每个进程发来的log并往/var/log里写,syslog函数将log写给rsyslogd进程,rsyslogd -v2.logrotate:logrotate /etc/logrotate.rsyslog(bb中重命名)2.2 rs…...

Nginx内存池源码剖析

能看出来Nginx小块内存和大块内存分界线就是一个页面4k(x86) #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)//能分配的最大内存#define NGX_DEFAULT_POOL_SIZE (16 * 1024)//默认池的大小 16k#define NGX_POOL_ALIGNMENT 16//字节对齐…...

【C++、数据结构】封装map和set(用红黑树实现)

文章目录📖 前言1. 如何复用同一棵红黑树⚡1.1 🌀 修改后结点的定义:2. 模拟实现中何实现数据比较大小🌟3. 红黑树迭代器的实现🔥3.1 💥红黑树begin()和 end(&#xff09…...

Python-项目实战--飞机大战-pygame快速入门(3)

4.理解精灵和精灵组4.1精灵和精灵组在刚刚完成的案例中,图像加载、位置变化、绘制图像都需要程序员编写代码分别处理为了简化开发步骤,pygame提供了两个类pygame.sprite.Sprite --存储图像数据 image和位置rect的对象pygame.sprite.Group注意&#xff1a…...

【2007NOIP普及组】T4.Hanoi双塔问题 试题解析

【2007NOIP普及组】T4.Hanoi双塔问题 试题解析 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 给定A,B,C三根足够长的细柱,在A柱上放有2n个中间有空的圆盘,共有n个不同的尺寸,每个尺寸都有两个相同的圆盘,注意这两个圆盘是不加区分的(下图为n=3的情形)。现要…...