防抖和节流滴实现🙈

226 阅读3分钟

防抖和节流

防抖

触发事件后函数在n秒内只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间;防抖函数又分为 非立即执行立即执行 两种版本。

应用场景

  • 搜索框提示:用户输入一串字符后,只会在最后一次输入的时候发送ajax请求;减少请求次数;
  • 按钮提交:多次点击提交按钮,只执行最后一次

1. 非立即执行

在连续输入的时候,当 输入的间隔 小于 所设置的时间间隔,这个事件只会执行一次,也就是最末尾的那一次;

  • 实现代码:

    通过一个高阶函数来实现。用context来保存this,使定时器调用函数funcfunc的this仍旧指向debounce中返回函数中的this;而在这里我们并不知道func会有多少个参数,所以用arguments来接收func的参数(apply的第二个参数接收的是数组);

    function debounce(func, delay) {
        let timeout;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, delay);
        }
    }
    

    以监听input输入为例,每次输入的时候都会清除当前的timeout,重新设置一个新的timeout,对触发函数进行延迟处理,直到结束连续输入,最后一次执行该防抖函数,在delay时间后执行func

    代码效果如下:

20210930_163619.gif

  • 可以看到,这种非立即执行实现防抖的方式是有缺陷的;在你完成输入的时候,还需要等待一小会才能得到页面的反馈,有点影响用户体验感。

  • 如果想要在一定时间之后立刻执行函数,等到停止

2. 立即执行

第一次操作会立即执行,下一次停止触发n秒后,再重新触发执行;

  • 实现代码

    function debounce(func, delay) {
        let timeout;
        return function() {
            const context = this;
            const args = arguments;
            if(timeout) clearTimeout(timeout);
            if(!timeout) func.apply(context, args);、
            timeout = setTimeout(function() {
                func.apply(context, args);
            }, delay)
        }
    }
    

    如果timeout存在,此时timeout就有个标识值,延迟完毕,定时器timeout为空时执行函数func

  • 代码效果如下:(布尔值为!timemout的打印结果)

20210930_181848.gif

节流

在规定的一段时间内,只能触发一次函数。如果在这段时间内多次触发函数,只有一次生效。

1. 使用时间戳实现

当触发事件的时候,取出当前的时间戳,再减去之前的时间戳;在两者相隔时间大于指定时间时再执行函数;

function throttle (func, delay) {
    let previous = 0;
    return function () {
        let now = +new Date();  // 设置时间戳
        const context = this;
        const args = arguments;
        if(now - previous > delay) {
            func.apply(context, args);
            previous = now;
        }
    }
}

2. 使用定时器实现

当事件触发的时候设置一个定时器,初始时!timeout === flase;也就是第一次直接触发函数,触发一次之后就有一个定时器;再次触发事件的时候如果定时器存在的话就不立刻执行,(在这段时间内重复触发,只有一个生效),直到定时器执行结束后执行函数并且清空定时器;

function throttle(func, delay) {
    let timeout;
    return function () {
        const context = this;
        const args = arguments;
        if (!timeout) {
            timeout = setTimeout(function () {
                timeout = null;
                func.apply(context, args)
            }, delay)
        }
    }
}

应用场景

  • 缩放:监控浏览器的resize事件,防止频繁触发大小改变
  • 拖拽:在一定时间内只执行一次

总结

防抖和节流都是防止在某一段时间内频繁的触发函数,不过这两者的原理不同

  • 防抖:将多次执行变为执行最后一次(若n秒内重复被触发,则重新计时)
  • 节流:将多次执行变为执行其中的一次(n秒内只执行一次)