throttle(事件节流)和 debounce(事件防抖)。 本质:这两个东西都以闭包的形式存在。它们通过对事件对应的回调函数进行包裹、以自由变量的形式缓存时间信息,最后用 setTimeout 来控制事件的触发频率。

    1. Throttle: 第一个人说了算

    throttle 的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。是通过在一段时间内无视后来产生的回调请求来实现的

    1. function throttle(fn, interval) {
    2. // last为上一次触发回调的时间
    3. let last = 0
    4. return function () {
    5. // 记录本次触发回调的时间
    6. let now = +new Date()
    7. if (now - last >= interval) {
    8. // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
    9. last = now;
    10. fn.apply(this., aruments);
    11. }
    12. }
    13. }
    14. const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
    15. document.addEventListener('scroll', better_scroll)
    1. Debounce: 最后一个人说了算

    防抖的中心思想在于:我会等你到底。在某段时间内,不管你触发了多少次回调,我都只认最后一次。

    1. function debounce(fn, delay) {
    2. // 定时器
    3. let timer = null
    4. return function () {
    5. clearTimeout(timer)
    6. // 设立新定时器
    7. timer = setTimeout(() => {
    8. fn.apply(this, arguments)
    9. }, delay)
    10. }
    11. }
    12. const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000)
    13. document.addEventListener('scroll', better_scroll)
    1. 用 Throttle 来优化 Debounce

    debounce 的问题在于它“太有耐心了”。试想,如果用户的操作十分频繁——他每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。
    为了避免弄巧成拙,我们需要借力 throttle 的思想,打造一个“有底线”的 debounce——等你可以,但我有我的原则:delay 时间内,我可以为你重新生成定时器;但只要delay的时间到了,我必须要给用户一个响应。这个 throttle 与 debounce “合体”思路,已经被很多成熟的前端库应用到了它们的加强版 throttle 函数的实现中:

    1. // fn是我们需要包装的事件回调, delay是时间间隔的阈值
    2. function throttle(fn, delay) {
    3. // last为上一次触发回调的时间, timer是定时器
    4. let last = 0, timer = null
    5. return function () {
    6. // 记录本次触发回调的时间
    7. let now = +new Date()
    8. // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    9. if (now - last < delay) {
    10. // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
    11. clearTimeout(timer)
    12. timer = setTimeout(() => {
    13. last = now
    14. fn.apply(this, arguments)
    15. }, delay)
    16. } else {
    17. // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
    18. last = now
    19. fn.apply(this, arguments)
    20. }
    21. }
    22. }
    23. const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
    24. document.addEventListener('scroll', better_scroll)