跳到主要内容

节流和防抖

函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段。  大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片。由于肉眼只能分辨出一定频率的变化,当高频率的扫描,人类是感觉不出来的。反而形成一种视觉效果,就是一张图。就像高速旋转的风扇,你看不到扇叶,只看到了一个圆一样。  同理,可以类推到js代码。在一定时间内,代码执行的次数不一定要非常多。达到一定频率就足够了。因为跑得越多,带来的效果也是一样。倒不如,把js代码的执行次数控制在合理的范围。既能节省浏览器CPU资源,又能让页面浏览更加顺畅,不会因为js的执行而发生卡顿。这就是函数节流和函数防抖要做的事。

函数节流是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。

  • 节流:简单来说就是节省资源的目的(降低频率),在一段时间内,核心代码只执行一次
    • 限制一个函数在一定时间内只能执行一次。

 函数防抖是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。

  • 防抖:在频繁的操作下,保证一段时间结束后,才能触发一次事件.如果一段事件未结束再次触发事件,就会重新开始计时 (只在最后一次事件后触发)

防抖作用:防止频繁触发带来的DOM抖动

节流​

  • 节流实现的方式有两种

    • 1、利用时间戳
    • 2、利用定时器setTimeout

    第一种是用时间戳来判断是否已到执行时间,记录上次执行的时间戳,然后每次触发事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经达到时间差(Xms) ,如果是则执行,并更新上次执行的时间戳,如此循环。

    第二种方法是使用定时器,比如当 scroll 事件刚触发时,打印一个 hello world,然后设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。

时间戳​

js
//时间戳
function throttle(func, wait = 2000) {
let previous = 0 //记录上一次时间
//不能使用箭头函数,否则会改变this的指向
return function (...args) {
let now = +new Date()
//间隔大于2000 才执行
if (now - previous > wait) {
previous = now
// 指向当前对象DOM (DOM事件this指向绑定事件的元素对象)
func.apply(this, args)
}
}
}

function onScroll() {
console.log('1')
}
document.querySelector('#root').onscroll = throttle(onScroll)

定时器​

注意:

  • 节流定时器实现的方式和防抖唯一区别就是,何时请除定时器timer?
    • 在防抖中,需要每次触发之前,如果存在定时器,则请除
    • 而节流中,触发之后,在清除定时器,这样就保证了一定时间间隔内执行一次
js
// 定时器
function throttle(func, wait = 2000) {
let timer
return function (...args) {
const _this = this
//timer等于空的时候 才执行
if (!timer) {
timer = setTimeout(() => {
func.apply(_this, args)
//func之后清空timer
timer = null
}, wait)
}
}
}

function onScroll() {
console.log('1')
}
document.querySelector('#root').onscroll = throttle(onScroll)

防抖​

  • 不断的点击操作,直到最后一次才触发

注意:

  • debounce返回的函数,不能使用箭头函数,否则this指向是window
  • 使用变量保存this指向来保存当前dom对象的指向, 通过apply来改变当前对象,还可以传递参数
  • 需要取消上一次定时器
js
//频繁触发情况下,只执行最后一次
function debounce(func, wait = 1000) {
let timer
return function (...args) {
const _this = this
//每次都取消上一个定时器
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
func.apply(_this, args)
}, wait)
}
}
function onScroll() {
console.log('1')
}
document.querySelector('#root').onclick = debounce(onScroll)

应用场景​

防抖

  • 搜索框
    • 如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力
  • scroll 事件
  • 表单验证

节流

  • 点击事件
    • 在某些情况下,需要限制用户的点击频率,例如按钮点击,以避免用户误操作或多次触发相同的操作
  • 鼠标事件
    • 在某些情况下,需要连续触发的事件,可以使用节流来控制事件处理函数的执行,以避免高频率的回调