本文转自:https://www.jianshu.com/p/f9f6b637fd6c

闭包的典型应用就是函数防抖和节流,本文详细介绍函数防抖和节流的应用场景和实现。

函数防抖(debounce)

函数防抖,就是指触发事件后,在 n 秒后只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数的执行时间。

简单的说,当一个动作连续触发,只执行最后一次。

打个比方,坐公交,司机需要等最后一个人进入才能关门。每次进入一个人,司机就会多等待几秒再关门。

函数节流(throttle)

限制一个函数在一定时间内只能执行一次

举个例子,乘坐地铁,过闸机时,每个人进入后3秒后门关闭,等待下一个人进入。

为了方便理解,我们首先通过一个可视化的工具,感受一下三种环境(正常情况、函数防抖情况 debounce、函数节流 throttle)下,对于mousemove事件回调的执行情况。

竖线的疏密代表事件执行的频繁程度。可以看到,正常情况下,竖线非常密集,函数执行很频繁。而 debounce (函数防抖)则很稀疏,只有当鼠标停止移动时,才会执行一次。throttle(函数节流)分布的较为均匀,每过一段时间就会执行一次。

常见的应用场景

函数防抖(debounce)的应用场景

连续的事件,只需触发一次的回调场景有:

  • 搜索框搜索输入。只需要用户最后一次输入完再发送请求
  • 手机号、邮箱格式的输入验证检测
  • 窗口大小的 resize 。只需窗口调整完成后,计算窗口的大小,防止重复渲染。

函数节流(throttle)的应用场景

间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚动到底部监听
  • 谷歌搜索框,搜索联想功能
  • 高频点击提交,表单重复提交
  • 省市信息对应字母快速选择

实现原理

函数防抖(debounce)

简单实现:

const debounce = (func, wait) => {
let timer
return () => {
clearTimeout(timer)
timer = setTimeout(func, wait);
}
}

函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 清除掉定时任务,重新定时。

封装:

function debounce(fn, delay = 500) {
// timer 是在闭包中的
let timer = null; return function() {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
} // test debounce 返回一个函数
input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(function () {
console.log(input1.value)
}, 600))

绑定事件解释:addEventListener 第一个参数是监听的事件,第二个参数是对应事件的回调函数。将 debounce 函数作为回调函数,这个 debounce 回调函数返回一个防抖之后的函数,因此实现了防抖的功能。

防抖解释:当 按下某个键的时候触发 keydown 事件,并执行回调。timer 默认为 null,在 return 的函数中定时器 timer 被赋值,如果在 delay 延迟之内再次触发了 keydown 事件,那么 timer 就会被重置为null...,当用户输入完成之后(delay 时间已过),那么就会触发 debounce 中的回调函数,也就是 keydown 最终要执行的事件。

函数节流(throttle)

简单实现

const throttle = (func, wait) => {
let timer; return () => {
if (timer) {
return
}
timer = setTimeout(() => {
func();
timer = null
}, wait)
}
}

函数节流的目的,是为了限制函数一段时间内只能执行一次。因此,通过使用定时任务,延时方法执行。在延时的时间内,方法若被触发,则直接退出方法。从而实现一段时间内只执行一次。

封装:

function throttle(fn, delay) {
let timer = null return function() {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
})
}
}
// test
let div1 = document.getElementById('div1')
div1.addEventListener('drag', throttle(function(e) {
console.log(e.offsetX, e.offsetY)
}, 100))

解释:如果 timer 存在,那就直接返回,不再往下执行了。这样就实现了一段时间内执行一次的目的。

异同比较

相同点:

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

不同点:

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

参考资料

详解防抖函数(debounce)和节流函数(throttle)的更多相关文章

  1. Kotlin——高级篇(二):高阶函数详解与标准的高阶函数使用

    在上面一个章节中,详细的讲解了Kotlin中关于Lambda表达式的语法以及运用,如果还您对其还不甚理解,请参见Kotlin--高级篇(一):Lambda表达式详解.在这篇文章中,多次提到了Kotli ...

  2. 这个贴子的内容值得好好学习--实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化

    感觉要DJANGO用得好,ORM必须要学好,不管理是内置的,还是第三方的ORM. 最最后还是要到SQL.....:( 这一关,慢慢练啦.. 实例详解Django的 select_related 和 p ...

  3. 函数防抖(Debounce)、函数节流 (Throttle)

    一篇介绍文章:https://zhuanlan.zhihu.com/p/38313717 演示示例:http://demo.nimius.net/debounce_throttle/ 函数防抖(Deb ...

  4. node源码详解(五) —— 在main函数之前 —— js和C++的边界,process.binding

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...

  5. 转载 :实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(一)

    在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然Q ...

  6. 详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化

    在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用. 1. ...

  7. 详解php的curl几个函数

    关于php的curl一系列函数,这里解释一下它们的作用. 在html中,我们可以通过form设置http的post和get提交,但假如我们获取的数据不是从html中来的,而是php脚本主动向其他服务器 ...

  8. JavaScript语法详解:if语句&for循环&函数

    本文首发于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. if语句 最基本的if语句 if语句的结构体:(格式) if (条件表达式) ...

  9. 详解递归(基础篇)———函数栈、阶乘、Fibonacci数列

    一.递归的基本概念 递归函数:在定义的时候,自己调用了自己的函数. 注意:递归函数定义的时候一定要明确结束这个函数的条件! 二.函数栈 栈:一种数据结构,它仅允许栈顶进,栈顶出,先进后出,后进先出.我 ...

随机推荐

  1. 盘点十大GIS相关算法

    1.道格拉斯-普克算法(Douglas–Peucker) 道格拉斯-普克算法(Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法.迭代适应点算法.分裂与合并算法)是将曲 ...

  2. 10 分钟讲完 QUIC 协议。

    建议阅读本文需要搭配作者 HTTP 相关文章食用. 历史 HTTP 系列文章: 看完这篇HTTP,跟面试官扯皮就没问题了 HTTP 2.0 ,有点炸 ! 这里先来回顾一下 HTTP 的发展过程.首先, ...

  3. Microsoft .NET Framework 远程执行代码漏洞

    受影响系统:Microsoft .NET Framework 4.8Microsoft .NET Framework 4.7.2Microsoft .NET Framework 4.7.1Micros ...

  4. java-数据类型拓展

    import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;public class Demo03 { public static ...

  5. VS code 设置中文语言环境(实现语言切换自由)

    1.打开vs code,然后按快捷键ctrl+shift+p,输入configure language>回车 2.选择Install Additional Languages... 3.在左侧选 ...

  6. ITerm让MAC终端更加灵活高效

    一.ITerm2简介 1.安装 iTerm2 是一款完全免费的,专为 Mac OS 用户打造的命令行应用.直接在官网上http://iterm2.com/ 下载并安装即可. 2.配置 设置ITerm为 ...

  7. AVRmega16 LED 例程

                                                                                      AVRmega16 LED 例程   ...

  8. Citus 分布式 PostgreSQL 集群 - SQL Reference(创建和修改分布式表 DDL)

    创建和分布表 要创建分布式表,您需要首先定义表 schema. 为此,您可以使用 CREATE TABLE 语句定义一个表,就像使用常规 PostgreSQL 表一样. CREATE TABLE ht ...

  9. 一个关于wait/notify与锁关系的探究

    wait/notify 机制是解决生产者消费者问题的良药.它的核心逻辑是基于条件变量的锁机制处理.所以,它们到底是什么关系?wait()时是否需要持有锁? notify()是否需要持有锁?先说答案:都 ...

  10. Blazor 003 : Razor的基础语法

    上文,我们通过剖析一个最简单的 Blazor WASM 项目,讲明白了 Razor 文件是什么,以及它被转译成 C#后长什么样子.也介绍了 Razor 中最简单的一个语法:Razor Expressi ...