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

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

内容简介:有时候会在项目开发中频繁地触发一些事件,如针对这个问题,一般有两个方案:防抖 (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)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Remote

Remote

Jason Fried、David Heinemeier Hansson / Crown Business / 2013-10-29 / CAD 26.95

The “work from home” phenomenon is thoroughly explored in this illuminating new book from bestselling 37signals founders Fried and Hansson, who point to the surging trend of employees working from hom......一起来看看 《Remote》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

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

HEX CMYK 互转工具