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 相关文章推荐
网页javascript精华代码集
Jan 24 Javascript
身份证号码前六位所代表的省,市,区, 以及地区编码下载
Apr 12 Javascript
突发奇想的一个jquery插件
Nov 19 Javascript
超详细的javascript数组方法汇总
Nov 21 Javascript
JavaScript计划任务后台运行的方法
Dec 18 Javascript
JavaScript+HTML5 canvas实现放大镜效果完整示例
May 15 Javascript
小程序如何使用分包加载的实现方法
May 22 Javascript
JavaScript 预解析的4种实现方法解析
Sep 03 Javascript
layui实现多图片上传并限制上传的图片数量
Sep 26 Javascript
详解Vue中的Props与Data细微差别
Mar 02 Javascript
使用 Jest 和 Supertest 进行接口端点测试实例详解
Apr 25 Javascript
从0搭建vue-cli4脚手架
Jun 17 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
法兰绒滤网冲泡
2021/03/03 冲泡冲煮
解决phpmyadmin 乱码,支持gb2312和utf-8
2006/11/20 PHP
PHP加密技术的简单实现
2016/09/04 PHP
简述php环境搭建与配置
2016/12/05 PHP
PHP基于cookie实现统计在线人数功能示例
2019/01/16 PHP
PHP抽象类和接口用法实例详解
2019/07/20 PHP
php数组指针函数功能及用法示例
2020/02/11 PHP
Thinkphp 框架配置操作之配置加载与读取配置实例分析
2020/05/15 PHP
PHP编程一定要改掉的5个不良习惯
2020/09/18 PHP
IE图片缓存document.execCommand("BackgroundImageCache",false,true)
2011/03/01 Javascript
jquery中dom操作和事件的实例学习 下拉框应用
2011/12/01 Javascript
intro.js 页面引导简单用法 分享
2013/08/06 Javascript
Bootstrap学习笔记之css组件(3)
2016/06/07 Javascript
js实现select选择框效果及美化
2016/08/19 Javascript
vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native
2018/08/25 Javascript
AngularJS修改model值时,显示内容不变的实例
2018/09/13 Javascript
基于vue实现一个神奇的动态按钮效果
2019/05/15 Javascript
Vue实现固定定位图标滑动隐藏效果
2019/05/30 Javascript
Python实现获取某天是某个月中的第几周
2015/02/11 Python
PyQt5每天必学之工具提示功能
2018/04/19 Python
Python实现字符串的逆序 C++字符串逆序算法
2020/05/28 Python
完美解决Pycharm无法导入包的问题 Unresolved reference
2018/05/18 Python
Python二维数组实现求出3*3矩阵对角线元素的和示例
2019/11/29 Python
pycharm下配置pyqt5的教程(anaconda虚拟环境下+tensorflow)
2020/03/25 Python
Tensorflow tf.nn.depthwise_conv2d如何实现深度卷积的
2020/04/20 Python
完美解决jupyter由于无法import新包的问题
2020/05/26 Python
使用Nibabel库对nii格式图像的读写操作
2020/07/01 Python
python打包生成so文件的实现
2020/10/30 Python
matplotlib事件处理基础(事件绑定、事件属性)
2021/02/03 Python
世界上最全面的汽车零部件和配件集合:JC Whitney
2016/09/04 全球购物
高尔夫球鞋、服装、手套和装备:FootJoy
2018/12/15 全球购物
编辑求职信样本
2013/12/16 职场文书
婚前保证书范文
2015/02/28 职场文书
爸爸的三轮车观后感
2015/06/16 职场文书
初任公务员培训心得体会
2016/01/08 职场文书
QT连接MYSQL数据库的详细步骤
2021/07/07 MySQL