深入理解JS函数节流和去抖动
一、什么是节流和去抖?
1、节流
节流就是拧紧水龙头让水少流一点,但是不是不让水流了。想象一下在现实生活中有时候我们需要接一桶水,接水的同时不想一直站在那等着,可能要离开一会去干一点别的事请,让水差不多流满一桶水的时候再回来,这个时候,不能把水龙头开的太大,不然还没回来水就已经满了,浪费了好多水,这时候就需要节流,让自己回来的时候水差不多满了。
那在JS里有没有这种情况呢,典型的场景是图片懒加载监听页面的scoll事件,或者监听鼠标的mousemove事件,这些事件对应的处理方法相当于水,由于scroll和mousemove在鼠标移动的时候会被浏览器频繁的触发,会导致对应的事件也会被频繁的触发(水流的太快了),这样就会造成很大的浏览器资源开销,而且好多中间的处理是不必要的,这样就会造成浏览器卡顿的现象,这时候就需要节流。
如何节流呢?我们无法做到让浏览器不去触发对应的事件,但是可以做到让处理事件的方法执行频率减少,从而减少对应的处理开销。
2、去抖
最早接触这个词应该是在高中物理里面学到的,有时候开关在在真正闭合之前可能会发生一些抖动现象,如果抖动的明显的话,对应的小灯泡可能会闪烁,把灯泡闪坏了不重要,万一把眼睛再给闪坏了可就麻烦了,这个时候就有去抖电路的出现。
而在我们的页面里,也有这种情况,假设我们的一个输入框,输入内容的同时可能会去后台查询对应的联想词,如果用户输入的同时,频繁的触发input事件,然后频繁的向后台发送请求,那么直到用户输入完成时,之前的请求都应该是多余的,假设网络慢一点,后台返回的数据比较慢,那么显示的联想词可能会出现频繁的变换,直到最后的一个请求返回。
如何去抖呢?这个时候就可以在一定时间内监听是否再次输入,如果没有再次输入则认为本次输入完成,发送请求,否则就是判定用户仍在输入,不发送请求。
3、节流和去抖区别
去抖和节流是不同的,因为节流虽然中间的处理函数被限制了,但是只是减少了频率,而去抖则把中间的处理函数全部过滤掉了,只执行规判定时间内的最后一个事件。
二、JS实现方式
1、节流:
/** 实现思路:
** 参数需要一个执行的频率,和一个对应的处理函数,
** 内部需要一个lastTime 变量记录上一次执行的时间
**/
function throttle(func, wait) {
let lastTime = null;// 为了避免每次调用lastTime都被清空,利用js的闭包返回一个function;此外声明为全局变量也可以
return function() {
let now = new Date();
// 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
if (now - lastTime - wait > ) {
func();
lastTime = now;
}
}
}
再看如何调用:
// 由于闭包的存在,调用会不一样
let throttleRun = throttle(() => {
console.log();
}, );
window.addEventListener('scroll', throttleRun);
这时候疯狂的滚动页面,会发现会400ms打印一个123,而没有节流的话会不断地打印, 你可以改变wait参数去感受下不同。
但是到这里,我们的节流方法是不完善的,因为我们的方法没有获取事件发生时的this对象,而且由于我们的方法简单粗暴的通过判断这次触发的时间和上次执行时间的间隔来决定是否执行回调,这样就会造成最后一次触发无法执行,或者用户出发的间隔确实很短,也无法执行,造成了误杀,所以需要对方法进行完善。
function throttle(func, wait) {
let lastTime = null;
let timeout;
return function() {
let context = this;
let now = new Date();
// 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
if (now - lastTime - wait > ) {
// 如果之前有了定时任务则清除
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
func.apply(context, arguments);
lastTime = now;
} else if (!timeout) {
timeout = setTimeout(() => {
// 改变执行上下文环境
func.apply(context, arguments);
}, wait);
}
};
}
这样我们的方法就相对完善了,调用方法和之前相同。
2、去抖:
去抖的方法,和节流思路一致,但是只有在抖动被判定结束后,方法才会得到执行。
function debounce(func, wait) {
let lastTime = null;
let timeout;
return function() {
let context = this;
let now = new Date();
// 判定不是一次抖动
if (now - lastTime - wait > ) {
setTimeout(() => {
func.apply(context, arguments);
}, wait);
} else {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(() => {
func.apply(context, arguments);
}, wait);
}
// 注意这里lastTime是上次的触发时间
lastTime = now;
}
}
这时候按照之前同样的方式调用,会发现无论怎么疯狂的滚动窗口,只有停止滚动时,才会执行对应的事件。
去抖和节流已经有很多成熟的js进行了实现,其大致思路基本是这样的。
3、分享:
我们再给大家分享一下网友的实现方法的代码:
方法一:
这种实现方式的思路很好理解:设置一个一间隔时间,比如50毫秒,以此时间为基准设置定时器,当第一次触发事件到第二次触发事件间隔小于50毫秒时,清除这个定时器,并设置一个新的定时器,以此类推,直到有一次事件触发后50毫秒内没有重复触发。
代码如下:
function debounce(method) {
clearTimeout(method.timer);
method.timer = setTimeout(function() {
method();
}, );
}
这种设计方式有一个问题:本来应该多次触发的事件,可能最终只会发生一次。具体来说,一个循序渐进的滚动事件,如果用户滚动太快速,或者程序设置的函数节流间隔时间太长,那么最终滚动事件会呈现为一个很突然的跳跃事件,中间过程都被节流截掉了。这个例子举的有点夸张了,不过使用这种方式进行节流最终是会明显感受到程序比不节流的时候“更突兀”,这对于用户体验是很差的。有一种弥补这种缺陷的设计思路。
方法二:
第二种实现方式的思路与第一种稍有差别:设置一个间隔时间,比如50毫秒,以此时间为基准稳定分隔事件触发情况,也就是说100毫秒内连续触发多次事件,也只会按照50毫秒一次稳定分隔执行。代码如下:
var oldTime = new Date().getTime();
var delay = ; function throttle1(method) {
var curTime = new Date().getTime();
if (curTime - oldTime >= delay) {
oldTime = curTime;
method();
}
}
相比于第一种方法,第二种方法也许会比第一种方法执行更多次(有时候意味着更多次请求后台,即更多的流量),但是却很好的解决了第一种方法清除中间过程的缺陷。因此在具体场景应根据情况择优决定使用哪种方法。
对于方法二,我们再提供另一种同样功能的写法:
var timer = undefined,
delay = ; function throttle2(method) {
if (timer) {
return;
}
method();
timer = setTimeout(function() {
timer = undefined;
}, delay);
}
深入理解JS函数节流和去抖动的更多相关文章
- JS 函数节流和去抖
1.什么是节流和去抖? 节流.就是拧紧水龙头让水少流一点,但是不是不让水流了.想象一下在现实生活中有时候我们需要接一桶水,接水的同时不想一直站在那等着,可能要离开一会去干一点别的事请,让水差不多流满一 ...
- 也谈js函数节流
1.什么是js函数节流 其本质就是不让某些代码(函数)在没有间断的情况下连续重复执行,目的就是让执行函数的请求停止了一段时间后才执行. 2.函数节流运用的场景 窗口大小的改变(resize事件),滚动 ...
- js 函数节流和防抖
js 函数节流和防抖 throttle 节流 事件触发到结束后只执行一次. 应用场景 触发mousemove事件的时候, 如鼠标移动. 触发keyup事件的情况, 如搜索. 触发scroll事件的时候 ...
- js 函数节流throttle 函数去抖debounce
1.函数节流throttle 通俗解释: 假设你正在乘电梯上楼,当电梯门关闭之前发现有人也要乘电梯,礼貌起见,你会按下开门开关,然后等他进电梯: 但是,你是个没耐心的人,你最多只会等待电梯停留一分钟: ...
- js函数节流和防抖的理解与实现
一:函数防抖1.理解:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间2.思路:每次触发事件时都取消之前的延时调用方法 3.实现: function debounce ...
- 浅析 JavaScript 的函数节流和去抖
现代网页的实现上,会有很多交互上的优化,比如常见的 滚动加载 ,输入联想 等等.他们的实现思路很简单,以滚动加载而言,无非就是去是增加一个滚动的事件监听,每次滚动判断当前的元素是否已经滚动到了用户的可 ...
- 深入理解JS函数中this指针的指向
函数在执行时,会在函数体内部自动生成一个this指针.谁直接调用产生这个this指针的函数,this就指向谁. 怎么理解指向呢,我认为指向就是等于.例如直接在js中输入下面的等式: console.l ...
- hover时显示可跟随鼠标移动的浮动框,运用函数节流与去抖进行优化
在很多笔试面试题中总能看到js函数去抖和函数节流,看过很多关于这两者的讨论,最近终于在一个需求中使用了函数去抖(debounce)和函数节流(throttle). 需要完成的效果是,鼠标在表格的单元格 ...
- JS函数节流和防抖
看JS高级程序设计时,了解到一个概念--函数节流,是为了防止在高频率触发某些事件导致浏览器崩溃.最近又了解到另一个概念,防抖,感觉和函数节流很像,也查看了很多篇博文,算是理解了. 区别: 函数节流:频 ...
随机推荐
- 七:zooKeeper开源客户端ZkClient的api测试
ZkClient是Gitthub上一个开源的ZooKeeper客户端.ZKClient在ZooKeeper原生API接口之上进行了包装,是一个更加易用的ZooKeeper客户端.同时ZKClient在 ...
- json转换工具——fastjson的使用
1.maven依赖<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson< ...
- javascript大神修炼记(2)——运算符
读者朋友们好,前面我已经大概的了解了Javascript的作用以及一些基本的函数声明与变量声明,今天我们就接着前面的内容讲解,我们就来看一下javscript的逻辑(正序,分支,循环)以及一些简单的运 ...
- centos 磁盘分区格式化与挂载
1 查看系统里硬盘信息fdisk -l 2 磁盘分区fdisk /dev/sdc输入m显示帮助a:命令指定启动分区:d:命令删除一个存在的分区:l:命令显示分区ID号的列表:m:查看fdisk命令帮助 ...
- Aras Innovator DB备份与还原
错误信息 确认到该问题是因为孤立帐号的问题,在解决孤立帐号之前,可以通过语句查看,另外,还原了DB后,系统不会自动创建原来的登陆帐号的,需要手动新增登陆帐号 #查看孤立帐号列表exec sp_chan ...
- Django CRM查询 XXX.object.filter() 常用用法总结
__gt 大于 __gte 大于等于 User.objects.filter(age__gt=10) // 查询年龄大于10岁的用户 User.objects.filter(age__gte=10) ...
- NYOJ 228 士兵杀敌(五)【差分标记裸题】
题目链接 所有元素初始值为0才能这么做: ①l--r全加1 a[l]++; a[r+1]--; 求一遍前缀和为元素本身. 求两遍前缀和为元素前缀和. #include<cstdio> #i ...
- 如何求先序排列和后序排列——hihocoder1049+洛谷1030+HDU1710+POJ2255+UVA548【二叉树递归搜索】
[已知先序.中序求后序排列]--字符串类型 #1049 : 后序遍历 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho在这一周遇到的问题便是:给出一棵二叉树的前序和 ...
- scrapy抓取拉勾网职位信息(一)——scrapy初识及lagou爬虫项目建立
本次以scrapy抓取拉勾网职位信息作为scrapy学习的一个实战演练 python版本:3.7.1 框架:scrapy(pip直接安装可能会报错,如果是vc++环境不满足,建议直接安装一个visua ...
- [POJ1980]Unit Fraction Partition(搜索)
Unit Fraction Partition Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 4571 Accepted ...