js 防抖节流
# js 防抖
- 函数防抖(debounce)
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
# 定时器实现
// 防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if (timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
};
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener("scroll", debounce(handle, 1000));
2
3
4
5
6
7
8
9
10
11
12
13
14
当持续触发 scroll 事件时,事件处理函数 handle 只在停止滚动 1000 毫秒之后才会调用一次,也就是说在持续触发 scroll 事件的过程中,事件处理函数 handle 一直没有执行。
# 防抖应用场景
- 按钮提交场景: 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
- 搜索框模糊匹配场景: 防止多次输入事件多次发送请求,只发送最后一次输入.
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
- 编辑器实时保存,当无任何更改操作一秒后进行保存
# js 节流
- 函数节流(throttle)
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
# 时间戳实现
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
};
};
function handle() {
console.log(Math.random());
}
window.addEventListener("scroll", throttle(handle, 1000));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
当高频事件触发时,第一次会立即执行(给 scroll 事件绑定函数与真正触发事件的间隔一般大于 delay,如果你非要在网页加载 1000 毫秒以内就去滚动网页的话,我也没办法 o(╥﹏╥)o),而后再怎么频繁地触发事件,也都是每 delay 时间才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了 (最后一次触发事件与倒数第二次触发事件的间隔小于 delay,为什么小于呢?因为大于就不叫高频了呀(╹▽╹))。
# 定时器实现
// 节流throttle代码(定时器):
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
};
};
function handle() {
console.log(Math.random());
}
window.addEventListener("scroll", throttle(handle, 1000));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到 delay 时间后,定时器执行执行函数,并且清空定时器,这样就可以设置下个定时器。当第一次触发事件时,不会立即执行函数,而是在 delay 秒后才执行。而后再怎么频繁触发事件,也都是每 delay 时间才执行一次。当最后一次停止触发后,由于定时器的 delay 延迟,可能还会执行一次函数。
# 定时器加时间戳实现
节流中用时间戳或定时器都是可以的。更精确地,可以用时间戳+定时器,当第一次触发事件时马上执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
// 节流throttle代码(时间戳+定时器):
var throttle = function(func, delay) {
var timer = null;
var startTime = Date.now();
return function() {
var curTime = Date.now();
var remaining = delay - (curTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
};
};
function handle() {
console.log(Math.random());
}
window.addEventListener("scroll", throttle(handle, 1000));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在节流函数内部使用开始时间 startTime、当前时间 curTime 与 delay 来计算剩余时间 remaining,当 remaining<=0 时表示该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数和每隔 delay 时间执行一次事件处理函数)。如果还没到时间的话就设定在 remaining 时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。当然在 remaining 这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个 remaining 来判断当前状态。
# 节流应用场景
- 搜索联想功能
- scroll 事件,每隔一秒计算一次位置信息等
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
# 区别
防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
PS:防抖和节流能有效减少浏览器引擎的损耗,防止出现页面堵塞卡顿现象,