前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >函数防抖和节流

函数防抖和节流

作者头像
木子星兮
发布2020-07-16 19:32:03
5320
发布2020-07-16 19:32:03
举报
文章被收录于专栏:前端小码农前端小码农

函数防抖(debounce)

防抖:不管事件触发频率多高,一定在事件触发 n 秒后才执行,如果在一个事件执行的 n秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,触发完事件 n 秒内不再触发事件,n秒后再执行。

思路:

  1. 返回一个函数;
  2. 每次触发事件时都取消之前的定时器

需要注意问题:

  1. this指向
  2. 参数的传递
  3. 是否要立即调用一次
代码语言:javascript
复制
function debounce(fn, wait, immediate) {
    let timer = null;
    //  返回一个函数
    return function(...args) {
        // 每次触发事件时都取消之前的定时器
        clearTimeout(timer);
        // 判断是否要立即执行一次
        if(immediate && !timer) {
            fn.apply(this, args);
        }
        // setTimeout中使用箭头函数,就是让 this指向 返回的该闭包函数,而不是 debounce函数的调用者
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, wait)
    }
}

通过闭包保存一个标记(timer)来保存setTimeout返回的值, 每当要触发函数的时候, 需要先把上一个setTimeout清除掉, 然后再创建一个新的setTimeout, 这样就能保证执行函数后的 wait 间隔内如果还要触发函数, 就不会执行fn

使用场景

  1. 监听resize或scroll,执行一些业务处理逻辑
代码语言:javascript
复制
window.addEventListener('resize', debounce(handleResize, 200));
window.addEventListener('scroll', debounce(handleScroll, 200));

使用到一些高频触发的函数,需要考虑防抖

  • window 的 resize、scroll
  • mousedown、mousemove
  • keyup、keydown ...
  1. 搜索输入框,在输入后200毫秒搜索
代码语言:javascript
复制
debounce(fetchSearchData, 200);

可以这样去理解记忆:函数防抖是 在事件触发 n 秒后才执行,在监听 scroll事件和 resize 事件时,只要 n 秒后才执行一次就可以了,不需要每次只要一触发 scrollresize的时候就执行,n秒内的执行是没有意义的(用户可能都感受不到,而且很容易造成卡顿)。

函数节流(throttle)

函数节流:不管事件触发频率有多高,只在单位时间内执行一次。

有两种思路实现:使用时间戳和定时器

使用时间戳

代码语言:javascript
复制
function throttle(fn, wait)  {
    // 记录上一次执行的时间戳
    let previous = 0;
    return function(...args) {
        // 当前的时间戳,然后减去之前的时间戳,大于设置的时间间隔,就执行函数,否则不执行
        if(Date.now() - previous > wait) {
            // 更新上一次的时间戳为当前时间戳
            previous = Date.now();
            fn.apply(this, args);
        }
    }
}

第一次事件肯定触发,最后一次不会触发(比如说监听 onmousemove,则鼠标停止移动时,立即停止触发事件)

使用定时器

代码语言:javascript
复制
function throttle(fn, wait)  {
    // 设置一个定时器
    let timer = null;
    return function(...args) {
        // 判断如果定时器不存在就执行,存在则不执行
        if(!timer) {
            // 设置下一个定时器
            timer = setTimeout(() => {
                // 然后执行函数,清空定时器
                timer = null;
                fn.apply(this, args)
            }, wait)
        }
    }
}

第一次事件不会触发(fn是放在 setTimeout中执行的,所以第一次触发事件至少等待 wait 毫秒之后才执行),最后一次一定触发

定时器和时间戳结合

两者结合可以实现,第一次事件会触发,最后一次事件也会触发

代码语言:javascript
复制
function throttle(fn, wait)  {
    // 记录上一次执行的时间戳
    let previous = 0;
    // 设置一个定时器
    let timer = null;
    return function(...args) {
        // 当前的时间戳,然后减去之前的时间戳,大于设置的时间间隔
        if(Date.now() - previous > wait) {
            clearTimeout(timer);
            timer = null
            // 更新上一次的时间戳为当前时间戳
            previous = Date.now();
            fn.apply(this, args);
        } else if(!timer) {
            // 设置下一个定时器
            timer = setTimeout(() => {
                timer = null;
                fn.apply(this, args)
            }, wait)
        }
    }
}

参考

  • JavaScript专题之跟着underscore学防抖[1]
  • JavaScript专题之跟着 underscore 学节流[2]
  • 防抖和节流[3]

参考资料

[1]JavaScript专题之跟着underscore学防抖: https://github.com/mqyqingfeng/Blog/issues/22

[2]JavaScript专题之跟着 underscore 学节流: https://github.com/mqyqingfeng/Blog/issues/26

[3]防抖和节流: http://www.conardli.top/docs/JavaScript/%E9%98%B2%E6%8A%96.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 牧码的星星 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数防抖(debounce)
    • 使用场景
    • 函数节流(throttle)
      • 使用时间戳
        • 使用定时器
          • 定时器和时间戳结合
          • 参考
            • 参考资料
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档