资讯专栏INFORMATION COLUMN

函数的防抖和节流是个啥???

edagarli / 1917人阅读

摘要:函数防抖和节流,都是控制事件触发频率的方法。封装一个函数,让持续触发的事件监听是我们封装的这个函数,将目标函数作为回调传进去,等待一段时间过后执行目标函数第二点实现了,再看第一点持续触发不执行。

曾经面试时候被问到过这个,年少的我一脸无知。。。

后来工作中遇到了一个场景:输入名称的同时去服务器校验名称是否重复,但发现之前的代码竟然都没做限制,输入一次发一次请求。简直忍不了,就在项目的utils里加上了防抖函数。
正好做一个总结,加深印象。

函数防抖和节流,都是控制事件触发频率的方法。应用场景有很多,输入框持续输入,将输入内容远程校验、多次触发点击事件、onScroll等等。
为了说明问题,假设一个场景:鼠标滑过一个div,触发onmousemove事件,它内部的文字会显示当前鼠标的坐标。



效果是这样的:

在上边的场景下,我们不希望触发一次就执行一次,这就要用到防抖或节流。下面我们看一下它们能为我们做什么吧。

防抖

函数防抖,这里的抖动就是执行的意思,而一般的抖动都是持续的,多次的。假设函数持续多次执行,
我们希望让它冷静下来再执行。也就是当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的
一段时间之后,再去执行。先看一下效果:

分解一下需求:

持续触发不执行

不触发的一段时间之后再执行

那么怎么实现上述的目标呢?我们先看这一点:在不触发的一段时间之后再执行,那就需要个定时器呀,定时器里面调用我们要执行的函数,将arguments传入。
封装一个函数,让持续触发的事件监听是我们封装的这个函数,将目标函数作为回调(func)传进去,等待一段时间过后执行目标函数

function debounce(func, delay) {
  return function() {
    setTimeout(() => {
      func.apply(this, arguments)
    }, delay)
  }
}

第二点实现了,再看第一点:持续触发不执行。我们先思考一下,是什么让我们的函数执行了呢?是上边的setTimeout。OK,那现在的问题就变成了
持续触发,不能有setTimeout。这样直接在事件持续触发的时候,清掉定时器就好了。

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到时间了,再把开关打开,函数就会执行了。
我们看一下代码怎么实现:

  function throttle(func, deley) {
    let run = true
    return function () {
      if (!run) {
        return  // 如果开关关闭了,那就直接不执行下边的代码
      }
      run = false // 持续触发的话,run一直是false,就会停在上边的判断那里
      setTimeout(() => {
        func.apply(this, arguments)
        run = true // 定时器到时间之后,会把开关打开,我们的函数就会被执行
      }, deley)
    }
  }

调用的时候:

box.onmousemove = throttle(function (e) {
  box.innerHTML = `${e.clientX}, ${e.clientY}`
}, 1000)

这样,就实现了节流,节流还可以用时间间隔去控制,就是记录上一次函数的执行时间,与当前时间作比较,如果当前时间与上次执行时间的时间差大于一个值,就执行。

说明一下节流时,后面操作中应该是因为run=false 所以才直接return,但是不是在return之前let run=ture直接覆盖掉之前的false

这里可以看一下throttle函数内部,和函数调用的时候。首先看函数内部,分解一下结构:

function throttle(func, deley) {
    return function () {
      // 执行func
    }
}

那调用时候呢?也分解一下:

throttle(function () { // 目标函数内容 }, 1000)

这里throttle函数执行的结果是其内部return的function的调用。也就是说鼠标经过的事件监听实际上是这个被return的function,不断持续触发的是它,而throttle函数只是提供了一个作用域,内部用闭包声明了一个run的开关变量,由于闭包的存在,run这个变量会一直存在不被销毁,而let run = true只在这个闭包(可以理解为作用域)内只声明了一次,但它不会被持续执行,所以return的函数内部的判断不会被它覆盖掉。根据打印结果可以看出,事实确实是如此:

总结

防抖和节流巧妙地用了setTimeout,来控制函数执行的时机,优点很明显,可以节约性能,不至于多次触发复杂的业务逻辑而造成页面卡顿。

欢迎关注我的公众号: 一口一个前端,不定期分享我所理解的前端知识

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105490.html

相关文章

  • 从lodash源码学习节流防抖

    摘要:首先重置防抖函数最后调用时间,然后去触发一个定时器,保证后接下来的执行。这就避免了手动管理定时器。   之前遇到过一个场景,页面上有几个d3.js绘制的图形。如果调整浏览器可视区大小,会引发图形重绘。当图中的节点比较多的时候,页面会显得异常卡顿。为了限制类似于这种短时间内高频率触发的情况,我们可以使用防抖函数。   实际开发过程中,这样的情况其实很多,比如: 页面的scroll事件 ...

    CloudDeveloper 评论0 收藏0
  • 函数的防抖和节流

    摘要:当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的一段时间之后,再去执行原理第一次调用函数创建一个定时器,指定的时间间隔后运行代码。第二次调用函数时,它会清除前一次的定时器并设置另一个。 目的:节约性能开销,避免多次频繁的触发业务逻辑造成页面卡顿。 应用场景:节流和防抖的核心其实就是限制某一个方法被频繁触发,比如说DOM事件的监听回调,input的keyup、keydown...

    MasonEast 评论0 收藏0
  • 剖析前端开发中的防抖和节流

    摘要:运用防抖和节流可以有效降低代码的执行频率,从而解决高频率事件的页面卡顿问题。在阶段布局,最终确定显示的位置和大小。在函数中,首先定义了一个空的定时器变量,用来计算时间间隔。还有一点要注意,在中一定要清楚定时器,不然会影响的条件判断。 啥是节流? 节流是保证在一段时间内,代码只执行了一次。这个一段时间内指的是不管用户操作了几次,最终仅执行一次。比如说一个按钮,用户狂点按钮,但是如果用节流...

    andong777 评论0 收藏0
  • 剖析前端开发中的防抖和节流

    摘要:运用防抖和节流可以有效降低代码的执行频率,从而解决高频率事件的页面卡顿问题。在阶段布局,最终确定显示的位置和大小。在函数中,首先定义了一个空的定时器变量,用来计算时间间隔。还有一点要注意,在中一定要清楚定时器,不然会影响的条件判断。 啥是节流? 节流是保证在一段时间内,代码只执行了一次。这个一段时间内指的是不管用户操作了几次,最终仅执行一次。比如说一个按钮,用户狂点按钮,但是如果用节流...

    LeexMuller 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<