说说JavaScript中函数的防抖 (Debounce) 与节流 (Throttle)

栏目: JavaScript · 发布时间: 5年前

内容简介:有时候会在项目开发中频繁地触发一些事件,如针对这个问题,一般有两个方案:防抖 (Debounce)

为何要防抖和节流

有时候会在项目开发中频繁地触发一些事件,如 resizescrollkeyupkeydown 等,或者诸如输入框的实时搜索功能,我们知道如果事件处理函数无限制调用,会大大加重浏览器的工作量,有可能导致页面卡顿影响体验;后台接口的频繁调用,不仅会影响客户端体验,还会大大增加服务器的负担。而如果对这些调用函数增加一个限制,让其减少调用频率,岂不美哉?

针对这个问题,一般有两个方案:

防抖 (Debounce) 节流 (Throttle)

防抖(Debounce)

我对函数防抖的定义: 当函数被连续调用时,该函数并不执行,只有当其全部停止调用超过一定时间后才执行1次。

一个被经常提起的例子:

上电梯的时候,大家陆陆续续进来,电梯的门不会关上,只有当一段时间都没有人上来,电梯才会关门。

Talk is cheap,我们直接 show code 吧。

先做基本的准备(篇幅原因,HTML部分省略):

let container = document.getElementById('container');

// 事件处理函数
function handle(e) {
    onsole.log(Math.random()); 
}

// 添加滚动事件
container.addEventListener('scroll', handle);

我们发现,每滚动一下,控制台就会打印出一行随机数。

基础防抖

我们现在写一个最基础的防抖处理:

function debounce(func, wait) {
    var timeout;//标记
    return function() {
      clearTimeout(timeout);
      timeout = setTimeout(func, wait);
    }
}

事件也做如下改写:

container.addEventListener('scroll', debounce(handle, 1000));

现在试一下, 我们会发现只有我们停止滚动1秒钟的时候,控制台才会打印出一行随机数。

标准防抖

以上基础版本会有两个问题,请看如下代码:

// 处理函数
function handle(e) {
    console.log(this); //输出Window对象
    console.log(e); //undefined
}

没错,当我们不使用防抖处理时, handle() 函数的 this 指向调用此函数的container,而在外层使用防抖处理后,this的指向会变成Window。

其次,我们也要获取到事件对象 event

所以我们要对防抖函数做以下改写:

function debounce(fn, wait) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(()=>{
      fn.apply(this,arguments)//使用apply改变this指向
    }, wait);
  }
}

当然了,如果使用箭头函数便可以省去外层声明。

先触发式防抖

以上的情况都是只有当连续触发停止后才执行,那如果我们想让事件第一次触发就执行,后面的连续触发都不执行,直到停止触发一段时间才可以再次触发(比如防止频繁点击),该如何处理呢?

那么可以利用同样的原理,稍作修改即可:

function debounce(fn, wait) {
    let timeout;
    return function(){
      let arg = arguments;
      let that = this;
      clearTimeout(timeout);
      !timeout && fn.apply(that,arg)
        timeout = setTimeout(function(){
          timeout = null;
        }, wait);
    }
}

节流 (Throttle)

顾名思义, 节流就是节约流量,将连续触发的事件稀释成预设评率。

比如每间隔1秒执行一次函数,无论这期间触发多少次事件。

这有点像公交车, 无论在站点等车的人多不多,公交车只会按时来一班,不会来一个人就来一辆公交车。

标准节流

function throttle(fn, wait) {
  let timeout; 
  return function () {
    if (!timeout) { 
      timeout = setTimeout(() => {
        timeout = null;
        fn.apply(this, arguments)
      }, wait)
    }
  }
}

用滚动事件来描述节流,其实是一个非常典型的场景,比如需要用滚动事件判断是否加载更多等。

先触发式节流

和防抖函数类似,以上的情况是先等待后触发,如果我们想让事件先触发后等待,该如何处理呢?网上大部分文章都告诉你用时间戳的方式去实现,其实只要像防抖一样稍作修改即可实现。

function throttle(fn, wait) {
  let timeout; 
  return function () {
    if (!timeout) { 
      fn.apply(this, arguments)
      timeout = setTimeout(() => {
        timeout = null;
      }, wait)
    }
  }
}

这样,我们就会发现第一次触发函数就会立即生效。

总结

关于防抖与节流,lodash、underscore等 工具 库都有完善的实现可以直接用,本没有必要造轮子。本文的目的仅仅是为了将其主要思想和实现思路展现出来。更重要的,知道防抖和节流的本质后,就知道在何时使用防抖或者节流,何时先触发或后触发。无论需求如何改变,都可以灵活的运用。


以上所述就是小编给大家介绍的《说说JavaScript中函数的防抖 (Debounce) 与节流 (Throttle)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

统计学习方法

统计学习方法

李航 / 清华大学出版社 / 2012-3 / 38.00元

详细介绍支持向量机、Boosting、最大熵、条件随机场等十个统计学习方法。一起来看看 《统计学习方法》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具