函数防抖和节流,都是控制事件触发频率的方法。应用场景有很多,输入框持续输入,将输入内容远程校验、多次触发点击事件、onScroll等等。
防抖
函数防抖,这里的抖动就是执行的意思,而一般的抖动都是持续的,多次的。假设函数持续多次执行,我们希望让它冷静下来再执行。也就是当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的一段时间之后,再去执行。
分解一下需求:
持续触发不执行
不触发的一段时间(最后一次点击)之后再执行
那么怎么实现上述的目标呢?我们先看这一点:在不触发的一段时间之后再执行,那就需要个定时器呀,定时器里面调用我们要执行的函数,将arguments传入。
封装一个函数,让持续触发的事件监听是我们封装的这个函数,将目标函数作为回调(func)传进去,等待一段时间过后执行目标函数
1 2 3 4 5 6 7 8
| function debounce(func, delay) { return function() { setTimeout(() => { func.apply(this, arguments) }, delay) } }
|
第二点实现了,再看第一点:持续触发不执行。我们先思考一下,是什么让我们的函数执行了呢?是上边的setTimeout。OK,那现在的问题就变成了持续触发,不能有setTimeout。这样直接在事件持续触发的时候,清掉定时器就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function debounce(func, delay) { let timeout return function() { clearTimeout(timeout) timeout = setTimeout(() => { func.apply(this, arguments) }, delay) } } 用法:
box.onmousemove = debounce(function (e) { box.innerHTML = `${e.clientX}, ${e.clientY}` }, 1000)
|
节流
节流的意思是让函数有节制地执行,而不是毫无节制的触发一次就执行一次。什么叫有节制呢?就是在一段时间内,只执行一次。
同样,我们分解一下:
持续触发并不会执行多次
(第一次执行之后)到一定时间再去执行
思考一下,持续触发,并不会执行,但是到时间了就会执行。抓取一个关键的点:就是执行的时机。要做到控制执行的时机,我们可以通过一个开关,与定时器setTimeout结合完成。
函数执行的前提条件是开关打开,持续触发时,持续关闭开关,等到setTimeout到时间了,再把开关打开,函数就会执行了。
我们看一下代码怎么实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function throttle(func, delay) { let run = true return function () { if (!run) { return } run = false setTimeout(() => { func.apply(this, arguments) run = true }, delay) } }
|
调用的时候:
1 2 3 4
| box.onmousemove = throttle(function (e) { box.innerHTML = `${e.clientX}, ${e.clientY}` }, 1000)
|
这样,就实现了节流,节流还可以用时间间隔去控制,就是记录上一次函数的执行时间,与当前时间作比较,如果当前时间与上次执行时间的时间差大于一个值,就执行。
总结
防抖和节流巧妙地用了setTimeout,来控制函数执行的时机,优点很明显,可以节约性能,不至于多次触发复杂的业务逻辑而造成页面卡顿。
介绍节流防抖原理、区别以及应用
防抖:多次触发事件,事件处理函数只能执行一次,并且是在触发操作结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定的时间(这时间是码农自己去定义的,比如 1 秒),如果没有再次被触发,那么就执行,如果被触发了,那就本次作废,重新从新触发的时间开始计算,并再次等待 1 秒,直到能最终执行
节流:事件触发后,规定时间内,事件处理函数不能再次被调用。也就是说在规定的时间内,函数只能被调用一次,且是最先被触发调用的那次
使用场景
防抖:搜索框搜索输入,并在输入完以后自动搜索、手机号,邮箱验证输入检测、窗口大小 resize 变化后,再重新渲染。
节流:滚动加载更多、搜索框搜的索联想功能、高频点击、表单重复提交……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
function debounce(fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function(){ fn.apply(this); }, delay); } } document.getElementById('btn').onclick = debounce(function () { console.log('按钮被点击了' + Date.now()); }, 1000); * 节流函数 一个函数执行一次后,只有大于设定的执行周期才会执行第二次。有个需要频繁触发的函数,出于优化性能的角度,在规定时间内,只让函数触发的第一次生效,后面的不生效。
* @param fn要被节流的函数 * @param delay规定的时间 */ function throttle(fn, delay) { var lastTime = 0; return function(){ var nowTime = Date.now(); if(nowTime - lastTime > delay){ fn.call(this); lastTime = nowTime; } } }
document.onscroll = throttle(function () { console.log('scllor事件被触发了' + Date.now()); }, 200);
|
参考文章:函数的防抖和节流是个啥???
介绍节流防抖原理、区别以及应用