likes
comments
collection
share

浅谈面试题之“防抖与节流”

作者站长头像
站长
· 阅读数 12

一个烂大街的面试题,已经理解了的权当温故知新,也欢迎大家在评论区发出更通透的理解。

在javaScript执行过程中,复杂的程序代码被封装到函数中,函数则不断被推入执行栈中。

函数中封装的代码块,一般都有相对复杂的逻辑处理(计算/判断),例如函数中可能会涉及到 DOM 的渲染更新,复杂的计算与验证, Ajax 数据请求等等。

如果浏览端将函数被频繁调用,造成的性能开销绝对不只一点点,不仅容易让客户电脑卡顿,还容易玩挂后端同学的接口,所以就要有相应的解决方案。那就是:  “防抖”  与  “节流”

防抖(debounce)

在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则延迟时间重新计时。

生活化理解:我们在玩英雄联盟时,回城需要8秒。如果你因为某种原因脱离了回城状态(被小兵打,自身移动了),若此时继续回城,则重新计算这8秒。这个过程,我们便可以理解为是防抖。

也就是说,当一个事件被触发,准备执行事件函数前,会等待一定的时间(这时间是码农自己去定义的,比如 1 秒),如果没有再次被触发,那么就执行,如果被触发了,那就本次作废,重新从新触发的时间开始计算,并再次等待 1 秒,直到能最终执行!

应用场景:搜索框搜索输入,并在输入完以后自动搜索、手机号,邮箱验证输入检测、窗口大小 resize 变化后,再重新渲染。


 const debounce = (handleTP, delay) => {
   let timer = null
   return (...args)=>{
     if(timer !== null) { // 第一次进入,timer没有被赋值,不会进入此判断。
       clearTimeout(timer) // 清空计时器,相当于打断回城
     }
     timer = setTimeout(()=>{ // 开始计时,相当于按下了回城键
       handleTP.call(undefined, ...args) // 调用回城方法
       timer = null
     }, delay)
   }
 }
 const fn = debounce( () => {console.log("我是个回城函数")}, 3000)

节流(throttle)

某一函数被触发后,若干时间内不能被再次执行。

生活化理解:游戏中的技能冷却时间,也就是技能CD,用一次后,过若干秒才能用下一次。

使用场景: 滚动加载更多、搜索框搜的索联想功能、高频点击、表单重复提交……

 const throttle = (fn, delay) => {
   let timer = null
   return (...args) => {
     if(timer) {return}  // 如果还在cd,就return
     fn.call(undefined, ...args) //放技能
     timer = setTimeout(()=>{
       timer = null // cd已过
     }, delay)
   }
 }

const fn = throttle( () => {console.log("我是个放技能函数")}, 3000)

“防抖” 与 “节流” 的异同

相同:都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。

区别:“防抖”是某一时间内只执行一次,最后一次触发后过段时间执行,而“节流”则是间隔时间执行,间隔时间固定。

关于setTimeout与clearTimeout的小补充

setTimeout() 方法的返回值不是空,而是一个唯一的数值,这个数值有什么用呢?

如果你想要终止 setTimeout() 方法的执行,那就必须使用 clearTimeout() 方法来终止

而使用这个方法的时候,因为你可能同时调用了好几个 setTimeout() 方法,所以系统必须知道你到底要终止的是哪一个 setTimeout() 方法

这样 clearTimeout() 方法就需要一个参数,这个参数就是 setTimeout() 方法的返回值 (数值),用这个数值来唯一确定结束哪一个 setTimeout() 方法。