js的防抖与节流

目录

  • 认识防抖与节流
    • 防抖
    • 节流
  • 手写防抖函数
    • 绑定this与参数
    • 取消功能
    • 立即执行
    • 获取返回值
    • 最终版
  • 手写节流函数

认识防抖与节流

JavaScript中,大量操作都会触发事件,这些事件又会被添加到事件队列中进行排队处理
某些事件如果频繁触发的话会对浏览器的性能造成损耗,我们就可以使用防抖或者节流操作来限制事件的执行频率

防抖

防抖即当一个事件被触发时不会立即执行,而是会等待一段时间后执行,在等待期间如果此事件被重复触发,则等待时间也会重新计算,只有在等待期间内没有事件触发,等待时间结束后才会触发事件的回调函数
简单地说,王者里的回城就是一种防抖

节流

节流即当事件触发时会执行事件的回调函数,之后这个事件将会等待一段时间,在这段时间内触发的事件都将不会执行,直到等待时间结束后如果依旧此事件触发才会执行此事件的回调函数,之后继续等待
简单地说,王者里的技能就是一种节流

如下图所示
结果

手写防抖函数

const debounce = (fn, delay) => {let timer = nullreturn () => {timer && clearTimeout(timer)timer = setTimeout(fn, delay)}
}
const resize = debounce(() => {console.log('resize')
}, 1000)
window.addEventListener('resize', resize)

结果如下
结果
但这个函数十分简陋,没有绑定this,也没有参数传递,接下来我们一步一步来实现这些功能

绑定this与参数

绑定this有两种方式,一种是通过在参数列表中传入this指向,然后通过显式绑定到对应的对象上,还有一种是将返回的箭头函数改成普通函数

  1. 第一种方式
const debounce = (fn, {delay,that}={}) => {let timer = nullreturn () => {timer && clearTimeout(timer)timer = setTimeout(fn.apply(that), delay)}
}
const resize = debounce(() => {console.log(this)}, {delay:1000,that:window
})
window.addEventListener('resize', resize)

这段代码粗看似乎没什么问题,但实测发现防抖函数无效,因为apply会立即调用函数,解决方法是将apply封装到一个函数中

const debounce = (fn, { delay, that } = {}) => {let timer = nullreturn () => {timer && clearTimeout(timer)timer = setTimeout(() => {fn.apply(that)}, delay)}
}
const resize = debounce(() => {console.log(this)}, {delay: 1000,that: window
})
window.addEventListener('resize', resize)

当我们需要传递的参数过多时可以通过参数解构与默认值的方式获取
2. 第二种方式

const debounce = (fn, delay) => {let timer = nullreturn function () {timer && clearTimeout(timer)timer = setTimeout(() => {fn.apply(this)}, delay)}
}
const resize = debounce(() => {console.log(this)}, 1000)
window.addEventListener('resize', resize)

最后结果都是一样的
结果
参数的绑定十分简单,这里就不再赘述了

const debounce = (fn, delay) => {let timer = nullreturn function (...args) {timer && clearTimeout(timer)timer = setTimeout(() => {fn.apply(this, args)}, delay)}
}
const resize = debounce((event) => {console.log(this, event)
}, 1000)
window.addEventListener('resize', resize)

取消功能

有时候事件触发了但我们之后又不想函数执行,可以增加一个取消功能,我们可以在返回的函数上直接添加一个属性cancel

const debounce = (fn, delay) => {let timer = nullconst _debounce = function (...args) {timer && clearTimeout(timer)timer = setTimeout(() => {fn.apply(this, args)}, delay)}_debounce.cancel = function () {timer && clearTimeout(timer)}return _debounce
}
const resize = debounce((event) => {console.log(this, event)
}, 1000)
window.addEventListener('resize', resize)

立即执行

立即执行即我们需要在事件触发时的第一次就执行函数

const debounce = (fn, { delay = 1000, immediate = false } = {}) => {let timer = nulllet isInvoke = falseconst _debounce = function (...args) {if (immediate && !isInvoke) {fn.apply(this, args)isInvoke = truereturn}timer && clearTimeout(timer)timer = setTimeout(() => {fn.apply(this, args)}, delay)}_debounce.cancel = function () {timer && clearTimeout(timer)}return _debounce
}
const resize = debounce((event) => {console.log(this, event)
}, {delay: 1000,immediate: true
})
window.addEventListener('resize', resize)

结果

获取返回值

有时候我们在手动调用防抖函数的时候需要得到函数的返回值就可以这么写,第一种方案是通过回调函数,第二种则是返回一个Promise

  1. 传入一个回调函数来获取返回值
const debounce = (fn, { delay = 1000, immediate = false, callback } = {}) => {let timer = nulllet isInvoke = falseconst _debounce = function (...args) {if (immediate && !isInvoke) {fn.apply(this, args)isInvoke = truereturn}timer && clearTimeout(timer)timer = setTimeout(() => {let res = fn.apply(this, args)callback && callback(res)}, delay)}_debounce.cancel = function () {timer && clearTimeout(timer)}return _debounce
}
const resize = debounce((event) => {console.log(this)return "resize return"
}, {delay: 1000,immediate: false,callback: (res) => {console.log(res)}
})
resize()
  1. 返回一个Promise得到返回值
const debounce = (fn, { delay = 1000, immediate = false } = {}) => {let timer = nulllet isInvoke = falseconst _debounce = function (...args) {return new Promise((resolve, reject) => {if (immediate && !isInvoke) {fn.apply(this, args)isInvoke = truereturn}timer && clearTimeout(timer)timer = setTimeout(() => {let res = fn.apply(this, args)resolve(res)}, delay)})}_debounce.cancel = function () {timer && clearTimeout(timer)}return _debounce
}
const resize = debounce((event) => {console.log(this)return "resize return"
}, {delay: 1000,immediate: false
})
resize().then((res) => {console.log(res)
})

结果都是一样的
结果

最终版

最后我们将以上代码优化一下就得到了最终版本的防抖函数

const debounce = (fn, { delay = 1000, immediate = false } = {}) => {let timer = nulllet isInvoke = falseconst _debounce = function (...args) {return new Promise((resolve, reject) => {try {if (immediate && !isInvoke) {let res = fn.apply(this, args)isInvoke = trueresolve(res)return}timer && clearTimeout(timer)timer = setTimeout(() => {let res = fn.apply(this, args)timer = nullisInvoke = falseresolve(res)}, delay)} catch (error) {reject(error)}})}_debounce.cancel = function () {timer && clearTimeout(timer)isInvoke = falsetimer = null}return _debounce
}

手写节流函数

节流函数在实现上和防抖函数稍有不同,不通过定时器而是通过时间戳
以下是一个简略的节流函数实现

const throttle = (fn, { wait = 1000, }) => {let preTime = 0;const _throttle = function (...args) {let nowTime = Date.now();if (nowTime - preTime > wait) {fn.apply(this, args);preTime = nowTime;}}return _throttle
}
const resize = throttle(function () {console.log("resize")
}, {wait: 1000,
})
window.addEventListener('resize', resize)

结果如下
结果
至于节流函数的一些优化:

  1. 节流函数立即执行与尾部执行
  2. 添加取消功能
  3. 获得返回值

与防抖函数的思路大差不差,这里就不再过多赘述,以下是完全版

const throttle = (fn, { wait = 1000, leading = true, trailing = false } = {}) => {let preTime = 0;let timerconst _throttle = function (...args) {return Promise((resolve, reject) => {try {let nowTime = Date.now();if (!leading && preTime == 0) {preTime = nowTime}let interval = wait - (nowTime - preTime)if (interval <= 0) {let res = fn.apply(this, args)resolve(res)if (timer) clearTimeout(timer)preTime = nowTimetimer = nullreturn}if (trailing && !timer) {timer = setTimeout(() => {let res = fn.apply(this, args)resolve(res)preTime = Date.now()timer = null}, interval)}} catch (error) {reject(error)}})}_throttle.cancel = function () {if (timer) clearTimeout(timer)preTime = 0timer = null}return _throttle
}

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

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

相关文章

HarmonyOS —— buildMode 设置(对比 Android Build Varient)

前言 在安卓中 Build Variant 主要依赖模块&#xff08;module&#xff09;中 build.gradle 的 BuildType 和 ProductFlavor 提供的属性和方法&#xff0c;我们可以使用 Build Type 可以配置不同的构建方式、ProductFlavor 主要用来进行多渠道打包。 在鸿蒙中要做到同样像效果…

计算机网络——第四层:传输层以及TCP UDP

1. 传输层的协议 1.1 TCP (传输控制协议) - rfc793 连接模式的传输。 保证按顺序传送数据包。 流量控制、错误检测和在数据包丢失时的重传。 用于需要可靠传输的应用&#xff0c;如网络&#xff08;HTTP/HTTPS&#xff09;、电子邮件&#xff08;SMTP, IMAP, POP3&#xff09;…

阿里云服务器4核8G配置最新优惠价格表(2024活动报价)

阿里云服务器4核8g配置云服务器u1价格是955.58元一年&#xff0c;4核8G配置还可以选择ECS计算型c7实例、计算型c8i实例、计算平衡增强型c6e、ECS经济型e实例、AMD计算型c8a等机型等ECS实例规格&#xff0c;规格不同性能不同&#xff0c;价格也不同&#xff0c;阿里云服务器网al…

uniapp uni.chooseLocation调用走失败那里,错误码:112

问题&#xff1a;我配置了百度上所有能配置的&#xff0c;一直调用不成功&#xff0c;如下图配置的 1:第一个 配置 代码&#xff1a; "permission": {"scope.userLocation": {"desc": "你的位置信息将用于小程序位置接口的效果展示"}…

C语言数据结构之线性表-顺序表篇

星光不负赶路人 江河眷顾奋楫者 &#x1f3a5;烟雨长虹&#xff0c;孤鹜齐飞的个人主页 &#x1f525;个人专栏 期待小伙伴们的支持与关注&#xff01;&#xff01;&#xff01; 线性表的简介# 线性表&#xff08;linearlist&#xff09;&#xff1a;是n个具有相同特性的数据元…

css实现动态水波纹效果

效果如下&#xff1a; 外层容器 (shop_wrap)&#xff1a; 设置外边距 (padding) 提供一些间距和边距 圆形容器 (TheCircle)&#xff1a; 使用相对定位 (position: relative)&#xff0c;宽度和高度均为 180px&#xff0c;形成一个圆形按钮圆角半径 (border-radius) 设置为 50%&…

面试题 05.06. 整数转换(力扣)(OJ题)

题目链接&#xff1a;面试题 05.06. 整数转换 - 力扣&#xff08;LeetCode&#xff09; 所属专栏&#xff1a;刷题 整数转换。编写一个函数&#xff0c;确定需要改变几个位才能将整数A转成整数B。 示例1: 输入&#xff1a;A 29 &#xff08;或者0b11101&#xff09;, B 15…

5-微信小程序语法参考

1. 数据绑定 官网传送门 WXML 中的动态数据均来自对应 Page 的 data。 数据绑定使用 Mustache 语法&#xff08;双大括号&#xff09;将变量包起来 ts Page({data: {info: hello wechart!,msgList: [{ msg: hello }, { msg: wechart }]}, })WXML <view class"vie…

搜索与图论第四期 树与图的广度优先遍历(例题)

例题&#xff1a;快速排序模板&#xff1a; AC代码&#xff1a; 源码&#xff1a; #include <iostream> using namespace std; const int N 1e6 10; int n; int q[N];void quick_sort(int q[], int l, int r) {if (l > r)return ;int x q[l], i l - 1, j r 1…

经典目标检测YOLO系列(二)YOLOV2的复现(1)总体网络架构及前向推理过程

经典目标检测YOLO系列(二)YOLOV2的复现(1)总体网络架构及前向推理过程 和之前实现的YOLOv1一样&#xff0c;根据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;在不脱离YOLOv2的大部分核心理念的前提下&#xff0c;重构一款较新的YOLOv2检测器&#xff0c;来对YOLOV2有…

压力测试+接口测试(工具jmeter)

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因 为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是…

【论文阅读】Deep Graph Contrastive Representation Learning

目录 0、基本信息1、研究动机2、创新点3、方法论3.1、整体框架及算法流程3.2、Corruption函数的具体实现3.2.1、删除边&#xff08;RE&#xff09;3.2.2、特征掩盖&#xff08;MF&#xff09; 3.3、[编码器](https://blog.csdn.net/qq_44426403/article/details/135443921)的设…

借用GitHub将typora图片文件快速上传CSDN

前情概要 众所周知&#xff0c;程序员大佬们喜欢用typora软件写代码笔记&#xff0c;写了很多笔记想要放到CSDN上给其他大佬分享&#xff0c;但是在往csdn上搬运的时候&#xff0c;图片总是上传出错&#xff0c;一张一张搞有很麻烦&#xff0c;咋如何搞&#xff1f; 废话不多…

muduo网络库剖析——监听者EpollPoller类

muduo网络库剖析——监听者EpollPoller类 前情从muduo到my_muduo 概要epoll原理解析epoll提供的接口epoll的触发模式epoll实现多路复用 框架与细节成员函数使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否…

SpringBoot 更新业务场景下,如何区分null是清空属性值 还是null为vo属性默认值?

先看歧义现象 值为null 未传递此属性 所以此时如何区分null 时传递进来的的null&#xff0c;还是属性的默认值null? 引入方案 引入过滤器&#xff0c;中间截获requestBodyData并保存到HttpServletRequest&#xff0c;业务层从HttpServletRequest 获取到requestBodyData辅…

LaTeX 多栏文档 Multiple columns如何插入图片并修改样式

在今天写报告的时候用到了 latex 的多栏列表&#xff0c;插入图片的时候感觉很无助 如果不喜欢让Latex自动安排图片位置&#xff0c;可以使用float包&#xff0c;然后可以使用\begin{figure}[H]。 记得提前导入这个包 \usepackage{float} 为了让我的图片的caption居中&#xf…

市面上常见硬盘分析及对比

固态硬盘 vs. 机械硬盘对比&#xff1a; 工作原理&#xff1a; 固态硬盘(SSD)&#xff1a; 使用非易失性存储器&#xff08;NAND闪存&#xff09;来存储数据&#xff0c;通过电子方式读写。机械硬盘(HDD)&#xff1a; 使用旋转的磁盘片和移动的磁头进行数据读写&#xff0c;依赖…

django电影推荐系统

电影推荐 启动 ./bin/pycharm.shdjango-admin startproject movie_recommendation_projectcd movie_recommendation_project/python manage.py movie_recommendation_apppython manage.py startapp movle_recommendation_applspython manage.py runserver Using the URLconf d…

Python多线程爬虫——数据分析项目实现详解

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 ChatGPT体验地址 文章目录 前言爬虫获取cookie网站爬取与启动CSDN爬虫爬虫启动将爬取内容存到文件中 多线程爬虫选择要爬取的用户 线程池 爬虫 爬虫是指一种自动化程序&#xff0c;能够模…

Kylin 安装novnc 远程访问

noVNC可以使用浏览器直接访问服务器&#xff0c;而不需要使用VNC客户端。 1.初始环境 关闭防火墙或允许IP访问本机 2.安装依赖 dnf install -y tigervnc-server git 3.git下载novnc git clone https://github.com/novnc/noVNC.git 4.配置信任证书 openssl req -new -x509 …