一篇彻底搞懂什么是函数防抖和节流,及应用场景

函数防抖和节流是优化高频率执行js代码的一种手段,js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover,input输入框的keypress等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。

1.防抖 (debounce)

防抖,顾名思义,防止抖动,以免把一次事件误执行多次,影响性能,比如敲键盘就是一个每天都会接触到的防抖操作。
应用场景

  1. 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
  2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
  3. 文本编辑器实时保存,当无任何更改操作一秒后进行保存
  4. mousemove、mouseover鼠标移动事件防抖
  5. 搜索框搜索输入。只需用户最后一次输入完,再发送请求防抖
  6. 手机号、邮箱验证输入检测

举例:onmousemove事件

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

初步实现防抖函数debounce代码如下:

function debounce(fn, delay) {
    let timer; // 维护一个 timer
    return function () {
        let args = arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(()=> {
            fn.apply(this, args); // 用apply指向调用debounce的对象,相当于this.fn(args);
        }, delay);
    };
}

测试用例:

// test
function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 给防抖函数传参
}

在document中鼠标移动的时候,会在onmousemove最后触发的1s后执行回调函数testDebounce;如果我们一直在浏览器中移动鼠标(比如10s),会发现会在10 + 1s后才会执行testDebounce函数(因为clearTimeout(timer),每次移动还没来得及执行timer就再次触发debounce然后clearTimeout(timer),这个就是函数防抖

在这里插入图片描述

最后用es6优化代码如下,可以看出来防抖重在清零 clearTimeout(timer)

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

2.节流 (throttle)

节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。
应用场景

  1. scroll 事件,滚动监听事件,每隔一秒计算一次位置信息等
  2. 浏览器播放事件,每个一秒计算一次进度信息等
  3. input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
  4. 高频点击提交,表单重复提交

初步实现节流函数封装:

function throttle(fn, delay) {
    let timer;
    return function () {
        let _this = this;
        let args = arguments;
        if (timer) {
            return;
        }
        timer = setTimeout(function () {
            fn.apply(_this, args);
            timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
        }, delay)
    }
}

测试用例:

function testThrottle(e, content) {
    console.log(e, content);
}
let testThrottleFn = throttle(testThrottle, 1000); // 节流函数
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 给节流函数传参
}

在这里插入图片描述

如上,若一直在浏览器中移动鼠标(比如10s),则在这10s内会每隔1s执行一次testThrottle。
函数节流的目的,是为了限制函数一段时间内只能执行一次。因此,定时器实现节流函数通过使用定时任务,延时方法执行。在延时的时间内,方法若被触发,则直接退出方法。从而,实现函数一段时间内只执行一次。

最后用es6代码如下,可以看出来节流重在加锁 timer=timeout

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

比较

  • 相同点:

    都可以通过使用 setTimeout 实现。
    目的都是,降低回调执行频率。节省计算资源。

  • 不同点:
    函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout 和 setTimeout实现。
    函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
    函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次。

总结

  • 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零clearTimeout。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。
  • 节流:控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。代码实现重在开锁关锁
    timer=timeout; timer=null。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。

你可能感兴趣的:(前端开发)