防抖与节流是两个出于性能考虑提出的概念,如果在短时间内多次触发同一函数,可能会产生页面的卡顿,以及性能的降低,针对这种情况,提出了防抖与节流两个解决方案。
防抖与节流的概念
为了解释如上的两个概念,我们以一个例子开始。有一个搜索框,当用户输入搜索内容时,会同时向后端发起请求,给出相近的词条以给用户提示
如果当每次用户输入一个字符都向都端发起一个请求,那么发起请求的次数就太频繁了,这时有两种策略:
- 当用户停止输入一段时间后,例如 500ms,我们向后端发起一个请求
- 每隔固定的时间向后端发起一个请求
第一种策略我们叫做防抖,第二种策略我们称为节流。
有一个形象的比喻,以公交车发车为例,有两种发车方案:
- 当乘客上车后,如果经过一段时间没有乘客上车了,就发车
- 发车时间固定,每隔一段时间发一次车
我们将发车比喻为向后端发起一个请求,那么这两种发车的策略就分别对应为防抖和节流。
防抖实现
我们要实现一个 debounce
函数,该函数接收一个函数以及时间,返回一个函数,debounce
函数的作用就是将一个普通的函数变为防抖的函数,即多次调用该函数,只有经过指定时间后,函数才会触发一次
const debounce = (fn, time) => {
let timerId
return function(...args) {
clearTimeout(timerId)
timerId = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
上面有几个注意事项:
timerId
要放在返回的函数之外,形成闭包,以确保每次调用函数时访问的是同一个timerId
返回的函数不要写成箭头函数的形式
// 不要写成这样
return (...args) => {
}因为这样会丢失
this
最后要通过
apply
方法将this
传入
节流实现
我们要实现一个 throttle
函数,该函数的作用是将一个普通函数转化为节流函数,也接收两个参数,第一个参数为函数,第二个函数为时间,当多次调用该函数,在一段时间内只能触发一次
const throttle = (fn, time) => {
let pre = 0;
return function(...args) {
let now = Date.now();
if (now - pre > time) {
fn.apply(this, args);
pre = now;
}
}
}
pre
表示上一次执行的时间,默认值为 0
,这样第一次调用该函数时会立即执行该函数。每次执行函数时,判断距上次执行已经经过的时间 Date.now() - start
是否大于设定的时间,大于则执行,小于则不执行,以达到节流的目的,执行函数后,设置 pre
为当前函数执行时的时刻。
note
返回的函数不能写为箭头函数,同样是因为会丢失 this
。