一、提出问题
博主在开发一个app时,某个页面可以通过输入关键字显示对应的信息,要求用户在输入的过程中和输入完成时输入框下方都要显示相关内容。也就是等于用户每更改一次input框的内容就要向后端查询一下,试想一下,如果用户因为打错字、或者随意更改内容,这个过程会向后端请求多少次?
细思极恐,于是我们需要采取一些限定手段,来减少性能消耗。也就是接下来提到的防抖和节流。
二、什么是防抖?
原理:第一次触发事件时,不立即执行函数,而是给出一个限定值,在限定值内没有再次触发同一事件则执行函数,如果在限定值内再次触发,则当前计时取消,重新开始计时。
效果:如果短时间内大量触发同一事件,只会执行一次函数。
实现:通过setTimeout+闭包,setTimeout用于计时,闭包用来存储时间变量
function debounce(fn, delay) { let timer = null return function (...args) { let context = this if (timer) clearTimeout(timer) timer = setTimeout(function () { fn.apply(context, args) }, delay) } }
三、什么是节流
原理:第一次触发事件时,执行对应的函数,并设置时间戳,再次触发事件时核验两次事件的触发间隔是否超过限定值,没有超过则不予执行,超过才执行
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
实现:时间戳+定时器
function throttle(func, delay) {
var timer = null; // 使用闭包,缓存变量
var prev = Date.now(); // 最开始执行的时间
return function() {
var context = this; // this指向window
var args = arguments;
var now = Date.now();
var remain = delay - (now - prev); // 剩余时间
clearTimeout(timer);
// 如果剩余时间小于0,就立刻执行
if (remain <= 0) {
func.apply(context, args);
prev = Date.now();
} else {
timer = setTimeout(func, remain);
}
}
}
在节流函数内部使用开始时间prev、当前时间now和剩余时间remain,当剩余时间小于等于0意味着执行处理函数,这样保证第一次就能立即执行函数并且每隔delay时间执行一次; 如果还没到时间,就会在remaining之后触发,保证最后一次触发事件也能执行函数,如果在remaining时间内又触发了该事件,那么会取消当前的计数器并计算出新的remaing时间。
定时器版本
function throttle(fn, delay) { let flag = true, timer = null return function (...args) { let context = this if (!flag) return flag = false clearTimeout(timer) timer = setTimeout(function () { fn.apply(context, args) flag = true }, delay) } }
四、区别以及应用场景
1.区别
函数防抖和函数节流都是防止某一事件频繁触发,但是这两兄弟之间的原理却不一样。 防抖是将多次执行变为只执行一次,节流是将多次执行变为每隔一段时间执行。
2.应用场景
(1)防抖
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
文本编辑器实时保存,当无任何更改操作一秒后进行保存
(2)节流
鼠标不断点击触发,mousedown(单位时间内只触发一次)
监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
input输入框,每个特定时间发送请求或是展开下拉列表
参考:https://www.cnblogs.com/ympjsc/p/11813691.html
https://www.cnblogs.com/momo798/p/9177767.html
https://segmentfault.com/a/1190000018428170