文章目录

  • 前言
  • 一、什么是防抖和节流?
  • 二、防抖
    • 简单介绍
    • 图解
    • 代码如下(示例):
  • 二、节流
    • 简单介绍
    • 图解
    • 代码如下(示例):
  • 三.防抖与节流的使用场景
  • 总结

前言

最近遇到注册登陆按钮需要添加防抖功能,在进行了简单了解和实践之后,将其整理记录下来。

一、什么是防抖和节流?

防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。针对此类快速连续触发和不可控的高频触发问题,debounce 和 throttle 给出了两种解决策略。在阅读本篇文章之前需要对闭包和超时调用(setTimeout)的相关知识有一定的了解。

二、防抖

简单介绍

防抖分为两种,一种是延迟执行的,叫做延迟防抖;另外一种是立即执行的,叫做前缘防抖。

注:(图解及部分代码思路参考自文末参考文档,个人觉得此图解较为清晰,另外,非常感谢前人留下的宝贵经验)

图解

延迟防抖图解:

前缘防抖图解:

代码如下(示例):

// 真正需要执行的函数,事件处理之类的
fn=()=>{console.log('发起网络请求');
}

1.一个简单的防抖函数

// 延迟防抖基础版debounceOne=(fn,delay)=>{  // 它只执行一次let timer = null;console.log('延迟防抖函数基础版')return(()=>{ // 匿名函数(闭包)clearTimeout(timer);timer = setTimeout(()=>{ //这里用箭头函数fn(); // 调用回调函数后将timer置空timer=null;},delay);})}

下面的代码中注意此处,this.debounceOne(this.func,2000)表示立即调用执行,一加载到debounceOne()这里就执行,然后当我们点击按钮的时候,执行的就是返回来的闭包函数了,而写在debounceOne函数中的非返回部分只在最初立即执行时执行一次,我们可以在那部分进行一些初始化工作。

<Button type='primary' onClick={this.debounceOne(this.fn,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(延迟防抖基础版)</Button>

2.在上面的基础上进行优化

// 延迟防抖优化版,不用多次清除,创建定时器,而是改变开始时间,在点击按钮到fn函数执行的期间内最多只创建了两次定时器,提高了性能debounceTwo = (fn,delay)=>{let timer = null;let triggerTime;console.log('延迟防抖函数优化版')let run =(wait)=>{timer = setTimeout(()=>{let executeTime = (new Date()).getTime();let alreadyWait = executeTime - triggerTime; // 计算开始时间戳(点击按钮时会被修改)与执行时间戳的差值if(alreadyWait < wait){run(delay-alreadyWait); //此处用delay不用wait}else {fn();timer = null;}},wait)}return (()=>{triggerTime = (new Date()).getTime(); // 点击按钮记录(修改)开始时间戳if(!timer){run(delay);}})}
<Button type='primary' onClick={this.debounceTwo(this.fn,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(延迟防抖优化版)</Button>

3.再向其中添加一个参数immediate,值为true时表示采用前缘防抖,该值也可由外部传入进行更改,具体代码有一些不同,下面有标注

  // 前缘防抖优化版debounceThree=(fn,delay,immediate=true)=>{let timer = null;let triggerTime;console.log('前缘防抖函数优化版'); // 只执行一次let run=(wait)=>{timer= setTimeout(()=>{let executeTime = (new Date()).getTime();let alreadyWait = executeTime-triggerTime; // 计算开始时间戳(点击时会被修改)与执行时间戳的差值if(alreadyWait < wait){run(delay-alreadyWait); //此处用delay}else {if(!immediate){ // 相对于延迟版本的变化fn();}// fn();timer = null;}},wait);}return (()=>{triggerTime = (new Date()).getTime(); // 点击记录(修改)开始时间戳if(!timer){if(immediate){ // 相对于延迟版本增加的部分fn();}run(delay);}})}
<Button type='primary' onClick={this.debounceThree(this.fn,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(前缘防抖优化版)</Button>

4.看到这里的话,基本上就可以理解防抖函数该怎么使用了。但是此时fn()函数是没有给它传入参数的,如果我们需要在fn()函数使用外部传入的参数,要怎样将参数传给fn()呢?来看接下来的拓展。

  //真正需要执行的函数,事件处理之类的,带arguments版本,使用普通函数funcWithArguments(){console.log('发起网络请求');console.log('传入参数:',arguments);// .......将arguments用于后续代码中}

拓展1:

// 延迟防抖优化带参数版,使用arguments,此处使用function 而不使用箭头函数debounceTwoWithArguments (fn,delay){let timer = null;let triggerTime;console.log('延迟防抖函数高性能带参数版');  // 只执行一次let run=function (wait){ //此处为了接受点击闭包时的arguments,用普通函数timer = setTimeout(()=>{let executeTime = (new Date()).getTime();let alreadyWait = executeTime - triggerTime; // 计算开始时间戳(点击时会被修改)与执行时间戳的差值if(alreadyWait < wait){run(delay-alreadyWait); //此处用delay更准确,不用wait}else {console.log('参数:',arguments);fn.apply(this,arguments);// fn();timer = null;}},wait)}return (function() { // 此处为了使用arguments,用普通函数triggerTime = (new Date()).getTime(); // 点击记录(修改)开始时间戳if(!timer){console.log(arguments); //点击时闭包函数的argumentsrun(delay,arguments); // 将闭包函数的arguments作为参数传入run,以供在run函数内的fn函数使用}})}
<Button type='primary' onClick={this.debounceTwoWithArguments(this.funcWithArguments,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(延迟防抖带参数优化版)</Button>

拓展2:

  // 前缘防抖优化带参数版,使用arguments,注意此时多处使用function 而不使用箭头函数debounceThreeWithArguments(fn,delay,immediate=true){let timer = null;let triggerTime;console.log('前缘防抖函数带参数优化版')let run=(wait)=>{timer= setTimeout(()=>{let executeTime = (new Date()).getTime();let alreadyWait = executeTime-triggerTime; // 计算开始时间戳(点击时会被修改)与执行时间戳的差值if(alreadyWait < wait){run(delay-alreadyWait); //此处用delay更准确,不用wait}else {if(!immediate){ // 相对于延迟版本的变化fn();}// fn();timer = null;}},wait);}return (function() {triggerTime = (new Date()).getTime(); // 点击记录(修改)开始时间戳if(!timer){if(immediate){ // 相对于延迟版本的增加// fn();//相比前缘优化版变动console.log('回调参数',arguments); // 获取到当我们点击按钮时,传入闭包函数的参数列表fn.apply(this,arguments);}run(delay);}})}
<Button type='primary' onClick={this.debounceThreeWithArguments(this.funcWithArguments,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(前缘防抖带参数优化版)</Button>

关于这里为什么使用function,原因在于箭头函数没有arguments属性,在需要传递未知数量的参数时用arguments会更方便,在使用时要注意this指向。另外一个地方是apply,一般用法为fn.apply(this,arguments),有兴趣的小伙伴可以去了解一下,这里不做详述。

二、节流

简单介绍

节流主要用于控制某项操作,在规定的时间(delay)内,只能执行一次,当经过了该时间周期后,才能再次执行。与防抖类似,节流也分为立即执行和非立即执行两种。

图解

延迟节流图解:

前缘节流图解:

代码如下(示例):

// 真正需要执行的函数,事件处理之类的
fn=()=>{console.log('发起网络请求');
}

1.节流函数,可选是否立即执行

//immediate=true表示前缘节流,否则为延迟节流throttle=(fn,delay,immediate=true)=>{let timer = null;return(()=>{if(!timer){if(immediate){fn();}timer=setTimeout(()=>{if(!immediate){fn();}timer = null;},delay)}})}
//一个简单的节流函数就完成了

下面的代码中注意此处,this.throttle(this.func,2000)表示立即调用执行,一加载到throttle()这里就执行,然后当我们点击按钮的时候,执行的就是返回来的闭包函数了,而写在throttle函数中的非返回部分只在最初立即执行时执行一次,我们可以在那部分进行一些初始化工作。

<Button type='primary' onClick={this.throttle(this.func,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(前缘节流版)</Button>

2.在上面的基础上进行拓展

// 真正需要执行的函数,事件处理之类的
funcWithArguments (){console.log('发起网络请求');console.log('参数arguments:',arguments);}
//immediate=true表示前缘节流,否则为延迟节流,带参数Arguments版throttleWithArguments (fn,delay,immediate=true){ // 使用普通函数let timer = null;return(function(){if(!timer){console.log(arguments); // 能够访问到点击后调用闭包函数时的arguments,该arguments可用于fn函数if(immediate){// fn();fn.apply(this,arguments); // arguments的使用,可将其传给fn函数}timer=setTimeout(function(){ // 此处需用普通函数,否则下面apply那里的this会有误if(!immediate){// fn();fn.apply(this,arguments); // arguments的使用,可将其传给fn函数}timer = null;},delay)}})}
<Button type='primary' onClick={this.throttleWithArguments(this.funcWithArguments,2000)} style={{marginBottom:'10px',display:'block'}}>点击发起请求(前缘节流带参数版)</Button>

三.防抖与节流的使用场景

函数节流与函数防抖都是为了限制函数的执行频率,防止函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象,但是需要根据实际场景需求采用不同的防抖和节流方式。
防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误触发多次造成资源浪费。如果事件是高频触发且有一定停顿,可用防抖进行处理,如用户在短时间内多次点击登陆,搜索框根据输入的一部分值进行联想搜索(y也可以使用节流),短信验证码,resize等。
节流:控制流量,单位时间内事件只能触发一次,如scroll 事件,mouseover事件,播放事件等。
当事件可能高频触发且只需要执行一次时可选用防抖;当事件可能高频触发,需要执行多次且触发相对平滑时最好使用节流。


总结

以上就是我对函数防抖与节流的理解,如有不同观点欢迎评论区留言交流,参考文档如下。
参考文档1
参考文档2
参考文档3

JavaScript防抖与节流的具体实现及使用场景相关推荐

  1. JavaScript防抖与节流

    JavaScript防抖与节流 1 为什么需要防抖和节流 2 防抖与节流原理 3 实现一个防抖函数 3.1 初步实现 3.2 this问题 3.3 event问题 3.4 立即执行 3.5 返回值问题 ...

  2. 前端战五渣学JavaScript——防抖、节流和rAF

    看了<JavaScript高级程序设计>和网上的一些博客,感觉对函数节流和函数防抖的概念是反的,以下我写的关于防抖和节流的概念取决于多数人的概念吧,并且基于伦敦前端工程师David Cor ...

  3. Javascript防抖函数节流函数的介绍(最详细)、彻底搞懂防抖节流的区别以及使用第三方库的防抖节流函数

    文章目录 1.防抖节流函数介绍 1.1 认识防抖节流函数 1.2 认识防抖函数 1.3 防抖函数应用 1.4 认识节流函数 1.5 节流函数应用 1.6 生活中的例子 2.Underscore库 2. ...

  4. javascript --- 防抖与节流

    说明 源码 1. 防抖与节流 1.1 防抖 防抖: 触发事件后,在n秒内函数只执行一次 记忆: 你手比较抖,不小心按了按钮2下-你只希望它只执行一次.且按第二次结束时间算-这就用到了防抖技术 1.2 ...

  5. javascript --- 防抖与节流

    先做一个监听鼠标移动的base: <style>#content{height:150px;width:200px;text-align:center;color:#fff;backgro ...

  6. 全网最详细JavaScript防抖、节流函数解析

    防抖与节流 防抖与节流 防抖 作用与简介 实现 核心与细节 应用场景 节流 作用与简介 实现 核心与细节 应用场景 防抖与节流 防抖 作用与简介 在一个周期T内,如果重复的进行某种响应操作,在不设置防 ...

  7. JavaScript 防抖和节流的实现

    一.防抖 1. 认识防抖 防抖:在第一次触发事件时,不立即执行函数,而是给出一个限定值,比如200ms,然后: 如果在200ms内没有再次触发事件,那么执行函数 如果在200ms内再次触发函数,那么当 ...

  8. 前端性能优化经典:javascript防抖节流

    ​ 大家好,我是前端岚枫,一枚二线城市的程序媛,今天主要跟大家分享我整理的前端的一些性能优化,js防抖节流,它是项目比较常见的性能优化方案,也是面试中常遇到的问题.希望下面文章对大家有所帮助. 我们在 ...

  9. mysql闭包的概念_彻底搞懂JavaScript的闭包、防抖跟节流

    最近出去面试了一下,收获颇多!!! 以前的我,追求实际,比较追求实用价值,然而最近面试,传说中的面试造火箭,工作拧螺丝,竟然被我遇到了.虽然很多知识点在实际工作中并不经常用到,但人家就是靠这个来筛选人 ...

最新文章

  1. javabean实体类与实体类之间的快速转换
  2. matlab中用于小数取整的函数的用法
  3. 杂谈 | 当前知识蒸馏与迁移学习有哪些可用的开源工具?
  4. xp系统电脑ntp服务器,xp 设置ntp服务器
  5. GitHub最火热的30个开源机器学习框架
  6. xshell快速发送命令操作多台linux
  7. dp打开思路:HDU1029 HDU1087 HDU1176 HDU1257 POJ1458(水题不水)
  8. 魅族15无法连接计算机,还在为数据丢失而烦恼?魅族15告诉你什么叫做碎屏无忧...
  9. Struts2值栈详解
  10. 如何查看华为服务器配置信息,华为服务器查看配置
  11. 解除Xcode中Miss File的警告
  12. GAN诞生记:最火的AI模型,来自一群博士的酒后争吵
  13. 集成学习 Bagging, Boosting, Stacking
  14. scratch3.0 整体页面介绍
  15. java PDF转jpg
  16. 华为手机进工程模式指令大全
  17. 2020校招复盘——秋招不易,致敬每一位追梦者(含网易、京东等19家互联网公司后台/Server端面经)
  18. 2016腾讯实习生招聘_基础研究 面试心得
  19. 笔记本的麦克风录不了音说不了话
  20. 一键卸载电脑自带Office2003

热门文章

  1. 神盾特工hive_《神盾局特工》第四季剧情:黛西黑化?
  2. 三生三世十里桃花手游怎么用电脑玩 三生三世十里桃花模拟器教程
  3. uniapp vue h5调用豆瓣接口图片无法显示403
  4. Markdown mermaid种草(3)_ 流程图
  5. Map<String,List<Map<String, Object>>>遍历
  6. python语言之父 是谁_Python之父与Python发展简史
  7. 【毕业季】送给学弟、妹的礼物。
  8. 牛客网 PTA乙级真题 1003 数素数
  9. 「Adobe国际认证」设计小白必须了解的色彩理论,绝对干货满满
  10. 【转】MBBMS CA方案