JS函数节流和防抖之间的区分和实现详解


Posted in Javascript onJanuary 11, 2019

在写JS时,这两个函数比较常见,有时候傻傻分不清用哪个,或者说知道代码要怎么写,但要说出它究竟是节流函数还是防抖函数时一脸楞逼。今天有一个同学分享了这两个的区分,我也来回顾一下,加深一下印象,以便日后用到时心里有底。PS:百度和谷歌搜索前几个介绍都是相反介绍,本文为原创,如有雷同纯属抄袭我的。

节流概念(Throttle)

按照设定的时间固定执行一次函数,比如200ms一次。注意:固定就是你在mousemove过程中,执行这个节流函数,它一定是200ms(你设定的定时器延迟时间)内执行一次。没到200ms,一定会返回,没有执行回调函数的。

主要应用场景有:scroll、touchmove

防抖概念(Debounce)

抖动停止后的时间超过设定的时间时执行一次函数。注意:这里的抖动停止表示你停止了触发这个函数,从这个时间点开始计算,当间隔时间等于你设定时间,才会执行里面的回调函数。如果你一直在触发这个函数并且两次触发间隔小于设定时间,则一定不会到回调函数那一步。

主要应用场景有:input验证、搜索联想、resize

节流实现

思路: 第一次先设定一个变量true,第二次执行这个函数时,会判断变量是否true,是则返回。当第一次的定时器执行完函数最后会设定变量为flase。那么下次判断变量时则为flase,函数会依次运行。

代码一:首次不执行

function throttle(fn,delay=100){
 //首先设定一个变量,在没有执行我们的定时器时为null
 let timer = null;
 return function(){
 //当我们发现这个定时器存在时,则表示定时器已经在运行中,需要返回
 if(timer) return;
 timer = setTimeout(()=>{
 fn.apply(this,arguments);
 timer = null;
 },delay);
 }
}

代码二:首次执行

function throttle2(fn,delay=100){
 let last = 0;
 return function(){
 let curr = +new Date();
 if(curr - last > delay){
 fn.apply(this,arguments);
 last = curr;
 }
 }
}

防抖实现

思路:首次运行时把定时器赋值给一个变量,第二次执行时,如果间隔没超过定时器设定的时间则会清除掉定时器,重新设定定时器,依次反复,当我们停止下来时,没有执行清除定时器,超过一定时间后触发回调函数。

代码一:首次不执行

function debounce(fn,delay=200){
 let timer = null;
 return function(){
 if(timer) clearTimeout(timer);
 timer = setTimeout(()=>{
 fn.apply(this,arguments);
 timer = null;
 },delay);
 }
}

代码二:首次执行

function debounce2(fn, delay = 200, atBegin = true) {
 let timer = null, last = 0,during;
 return function () {
 let self = this, args = arguments;
 var exec = function () {
 fn.apply(self, args);
 }
 if (atBegin && !timer) {
 exec();
 atBegin = false;
 } else {
 during = Date.now() - last;
 if (during > delay) {
 exec();
 } else {
 if (timer) clearTimeout(timer);
 timer = setTimeout(function () {
  exec();
 }, delay);
 }
 }
 last = Date.now();
 }
}

上面的代码只是我自己的一个简单实现,看看lodash里面的两个核心实现代码。生产中建议使用它们的库,毕竟有这么多人在用,出bug的机会比较少,我上面的代码有可能有一些情况没考虑到。如果你发现有问题的,也请告诉我。

如果在项目中有需要用到的,可以直接安装单个的NPM模块。throttle 和 debounce

lodash使用使用文档

lodash库里面这两个函数设置的参数有点复杂,记录一下里面的参数和代码使用。

节流(throttle)

官方文档解释:

创建一个节流函数,在 wait 秒内最多执行 func 一次的函数。 该函数提供一个 cancel 方法取消延迟的函数调用以及 flush 方法立即调用。 可以提供一个 options 对象决定如何调用 func 方法, options.leading 与|或 options.trailing 决定 wait 前后如何触发。 func 会传入最后一次传入的参数给这个函数。 随后调用的函数返回是最后一次 func 调用的结果。

注意: 如果 leading 和 trailing 都设定为 true 则 func 允许 trailing 方式调用的条件为: 在 wait 期间多次调用。

如果 wait 为 0 并且 leading 为 false, func调用将被推迟到下一个点,类似setTimeout为0的超时。

参数

func (Function)
要节流的函数

[wait=0] (number)
需要节流的毫秒

[options] (Object)
选项对象

[options.leading=true] (boolean)
指定调用在节流开始前

[options.trailing=true] (boolean)
指定调用在节流结束后

返回值 (Function)

返回节流的函数

示例

// 避免在滚动时过分的更新定位
jQuery(window).on('scroll', _.throttle(updatePosition, 100));

// 点击后就调用 `renewToken`,但5分钟内超过1次。
var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
jQuery(element).on('click', throttled);

// 取消一个 trailing 的节流调用
jQuery(window).on('popstate', throttled.cancel);

防抖(debounce)

创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 func 方法。 debounced(防抖动)函数提供一个 cancel 方法取消延迟的函数调用以及 flush 方法立即调用。 可以提供一个 options(选项) 对象决定如何调用 func 方法,options.leading 与 options.trailing 决定延迟前后如何触发(先调用后等待 还是 先等待后调用)。 func 调用时会传入最后一次提供给 debounced(防抖动)函数 的参数。 后续调用的 debounced(防抖动)函数返回是最后一次 func 调用的结果。

注意: 如果 leading 和 trailing 选项为 true, 则 func 允许 trailing 方式调用的条件为: 在 wait 期间多次调用防抖方法。

如果 wait 为 0 并且 leading 为 false, func调用将被推迟到下一个点,类似setTimeout为0的超时。

参数

func (Function)
要防抖动的函数

[wait=0] (number)
需要延迟的毫秒数

[options] (Object)
选项对象

[options.leading=false] (boolean)
指定调用在延迟开始前

[options.maxWait] (number)
设置 func 允许被延迟的最大值

[options.trailing=true] (boolean)
指定调用在延迟结束后

返回值 (Function)

返回具有防抖动功能的函数

示例

// 避免窗口在变动时出现昂贵的计算开销。
jQuery(window).on('resize', _.debounce(calculateLayout, 150));

// 当点击时 `sendMail` 随后就被调用。
jQuery(element).on('click', _.debounce(sendMail, 300, {
 'leading': true,
 'trailing': false
}));

// 确保 `batchLog` 调用1次之后,1秒内会被触发。
var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
var source = new EventSource('/stream');
jQuery(source).on('message', debounced);

// 取消一个 trailing 的防抖动调用
jQuery(window).on('popstate', debounced.cancel);

以上就是这篇节流和防抖的全部介绍,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
return false;和e.preventDefault();的区别
Jul 11 Javascript
javascript学习笔记(三) String 字符串类型介绍
Jun 19 Javascript
用jquery生成二级菜单的实例代码
Jun 24 Javascript
jQuery EasyUi实战教程之布局篇
Jan 26 Javascript
JavaScript中定义对象原型的两种使用方法
Dec 15 Javascript
微信小程序-拍照或选择图片并上传文件
Jan 06 Javascript
Angularjs自定义指令实现分页插件(DEMO)
Sep 16 Javascript
vue scroller返回页面记住滚动位置的实例代码
Jan 29 Javascript
Vue单页面应用保证F5强刷不清空数据的解决方案
Jan 31 Javascript
JavaScript学习笔记之DOM操作实例分析
Jan 08 Javascript
js回文数的4种判断方法示例
Jun 04 Javascript
Postman参数化实现过程及原理解析
Aug 13 Javascript
微信公众号H5支付接口调用方法
Jan 10 #Javascript
详解在Node.js中发起HTTP请求的5种方法
Jan 10 #Javascript
vue实现压缩图片预览并上传功能(promise封装)
Jan 10 #Javascript
微信小程序地图(map)组件点击(tap)获取经纬度的方法
Jan 10 #Javascript
最简单的JS实现json转csv的方法
Jan 10 #Javascript
puppeteer实现html截图的示例代码
Jan 10 #Javascript
其实你可以少写点if else与switch(推荐)
Jan 10 #Javascript
You might like
屏蔽浏览器缓存另类方法
2006/10/09 PHP
php中chdir()函数用法实例
2014/11/13 PHP
phpmailer绑定邮箱的实现方法
2016/12/01 PHP
PHP实现唤起微信支付功能
2019/02/18 PHP
PhpStorm 如何优雅的调试Hyperf的方法步骤
2019/11/24 PHP
使用Entrust扩展包在laravel 中实现RBAC的功能
2020/03/16 PHP
Prototype使用指南之enumerable.js
2007/01/10 Javascript
浅谈JavaScript编程语言的编码规范
2011/10/21 Javascript
js判断FCKeditor内容是否为空的两种形式
2013/05/14 Javascript
浅析showModalDialog数据缓存问题(用禁止浏览器缓存解决)
2013/07/09 Javascript
javascript和jquery修改a标签的href属性
2013/12/16 Javascript
浅析tr的隐藏和显示问题
2014/03/05 Javascript
JS实现浏览器状态栏文字从右向左弹出效果代码
2015/10/27 Javascript
微信小程序 页面传参实例详解
2016/11/16 Javascript
移动端日期插件Mobiscroll.js使用详解
2016/12/19 Javascript
javaScript产生随机数的用法小结
2018/04/21 Javascript
vue.js高德地图实现热点图代码实例
2019/04/18 Javascript
layui 上传图片 返回图片地址的方法
2019/09/26 Javascript
vue等两个接口都返回结果再执行下一步的实例
2020/09/08 Javascript
python基于urllib实现按照百度音乐分类下载mp3的方法
2015/05/25 Python
Python的Flask框架中集成CKeditor富文本编辑器的教程
2016/06/13 Python
玩转python selenium鼠标键盘操作(ActionChains)
2020/04/12 Python
利用Python实现Windows定时关机功能
2017/03/21 Python
基于python中pygame模块的Linux下安装过程(详解)
2017/11/09 Python
python selenium 获取标签的属性值、内容、状态方法
2018/06/22 Python
python3实现飞机大战
2020/11/29 Python
html5唤醒APP小记
2019/03/27 HTML / CSS
十一个高级MySql面试题
2014/10/06 面试题
毕业生个人求职信范例分享
2013/12/17 职场文书
初中升旗仪式演讲稿
2014/05/08 职场文书
十佳少年事迹材料
2014/12/25 职场文书
仓库保管员岗位职责
2015/02/09 职场文书
孕妇病假条怎么写
2015/08/17 职场文书
Django实现翻页的示例代码
2021/05/24 Python
js 数组 fill() 填充方法
2021/11/02 Javascript
JavaScript事件的委托(代理)的用法示例详解
2022/02/18 Javascript