防抖和节流
防抖
触发事件后函数在n秒内只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间;防抖函数又分为 非立即执行 和 立即执行 两种版本。
应用场景
- 搜索框提示:用户输入一串字符后,只会在最后一次输入的时候发送
ajax请求;减少请求次数; - 按钮提交:多次点击提交按钮,只执行最后一次
1. 非立即执行
在连续输入的时候,当 输入的间隔 小于 所设置的时间间隔,这个事件只会执行一次,也就是最末尾的那一次;
-
实现代码:
通过一个高阶函数来实现。用
context来保存this,使定时器调用函数func时func的this仍旧指向debounce中返回函数中的this;而在这里我们并不知道func会有多少个参数,所以用arguments来接收func的参数(apply的第二个参数接收的是数组);function debounce(func, delay) { let timeout; return function () { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(function () { func.apply(context, args) }, delay); } }以监听
input输入为例,每次输入的时候都会清除当前的timeout,重新设置一个新的timeout,对触发函数进行延迟处理,直到结束连续输入,最后一次执行该防抖函数,在delay时间后执行func;代码效果如下:
-
可以看到,这种非立即执行实现防抖的方式是有缺陷的;在你完成输入的时候,还需要等待一小会才能得到页面的反馈,有点影响用户体验感。
-
如果想要在一定时间之后立刻执行函数,等到停止
2. 立即执行
第一次操作会立即执行,下一次停止触发n秒后,再重新触发执行;
-
实现代码
function debounce(func, delay) { let timeout; return function() { const context = this; const args = arguments; if(timeout) clearTimeout(timeout); if(!timeout) func.apply(context, args);、 timeout = setTimeout(function() { func.apply(context, args); }, delay) } }如果
timeout存在,此时timeout就有个标识值,延迟完毕,定时器timeout为空时执行函数func; -
代码效果如下:(布尔值为
!timemout的打印结果)
节流
在规定的一段时间内,只能触发一次函数。如果在这段时间内多次触发函数,只有一次生效。
1. 使用时间戳实现
当触发事件的时候,取出当前的时间戳,再减去之前的时间戳;在两者相隔时间大于指定时间时再执行函数;
function throttle (func, delay) {
let previous = 0;
return function () {
let now = +new Date(); // 设置时间戳
const context = this;
const args = arguments;
if(now - previous > delay) {
func.apply(context, args);
previous = now;
}
}
}
2. 使用定时器实现
当事件触发的时候设置一个定时器,初始时!timeout === flase;也就是第一次直接触发函数,触发一次之后就有一个定时器;再次触发事件的时候如果定时器存在的话就不立刻执行,(在这段时间内重复触发,只有一个生效),直到定时器执行结束后执行函数并且清空定时器;
function throttle(func, delay) {
let timeout;
return function () {
const context = this;
const args = arguments;
if (!timeout) {
timeout = setTimeout(function () {
timeout = null;
func.apply(context, args)
}, delay)
}
}
}
应用场景
- 缩放:监控浏览器的
resize事件,防止频繁触发大小改变 - 拖拽:在一定时间内只执行一次
总结
防抖和节流都是防止在某一段时间内频繁的触发函数,不过这两者的原理不同
- 防抖:将多次执行变为执行最后一次(若n秒内重复被触发,则重新计时)
- 节流:将多次执行变为执行其中的一次(n秒内只执行一次)