详解防抖函数(debounce)和节流函数(throttle)
本文转自: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 实现。函数节流,是在一段连续操作中,每一段时间只执行一次,在频率较高的事件中使用来提高性能。
- 函数防抖关注一段时间内连续触发,只在最后一次执行;而函数节流侧重于在一段时间内只执行一次。
参考资料
- 函数节流与函数防抖
- SF - 函数节流与函数防抖
- JavaScript 函数节流和函数去抖应用场景辨析
- 浅谈 Underscore.js 中 _.throttle 和 _.debounce 的差异
- 可视化展示 debounce & throttle
- Debouncing and Throttling Explained Through Examples
- source-code-learning
- 实例解析防抖动(Debouncing)和节流阀(Throttling)
- lodash - debounce 源码
- https://segmentfault.com/a/1190000018445196
详解防抖函数(debounce)和节流函数(throttle)的更多相关文章
- Kotlin——高级篇(二):高阶函数详解与标准的高阶函数使用
在上面一个章节中,详细的讲解了Kotlin中关于Lambda表达式的语法以及运用,如果还您对其还不甚理解,请参见Kotlin--高级篇(一):Lambda表达式详解.在这篇文章中,多次提到了Kotli ...
- 这个贴子的内容值得好好学习--实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
感觉要DJANGO用得好,ORM必须要学好,不管理是内置的,还是第三方的ORM. 最最后还是要到SQL.....:( 这一关,慢慢练啦.. 实例详解Django的 select_related 和 p ...
- 函数防抖(Debounce)、函数节流 (Throttle)
一篇介绍文章:https://zhuanlan.zhihu.com/p/38313717 演示示例:http://demo.nimius.net/debounce_throttle/ 函数防抖(Deb ...
- node源码详解(五) —— 在main函数之前 —— js和C++的边界,process.binding
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...
- 转载 :实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化(一)
在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用.虽然Q ...
- 详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
在数据库有外键的时候,使用 select_related() 和 prefetch_related() 可以很好的减少数据库请求的次数,从而提高性能.本文通过一个简单的例子详解这两个函数的作用. 1. ...
- 详解php的curl几个函数
关于php的curl一系列函数,这里解释一下它们的作用. 在html中,我们可以通过form设置http的post和get提交,但假如我们获取的数据不是从html中来的,而是php脚本主动向其他服务器 ...
- JavaScript语法详解:if语句&for循环&函数
本文首发于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. if语句 最基本的if语句 if语句的结构体:(格式) if (条件表达式) ...
- 详解递归(基础篇)———函数栈、阶乘、Fibonacci数列
一.递归的基本概念 递归函数:在定义的时候,自己调用了自己的函数. 注意:递归函数定义的时候一定要明确结束这个函数的条件! 二.函数栈 栈:一种数据结构,它仅允许栈顶进,栈顶出,先进后出,后进先出.我 ...
随机推荐
- Java的jmap命令使用详解
jmap命令简介 jmap(Java Virtual Machine Memory Map)是JDK提供的一个可以生成Java虚拟机的堆转储快照dump文件的命令行工具.除此以外,jmap命令还可以查 ...
- 官宣 .NET 7 Preview 2
今天,我们很高兴发布 .NET 7 预览版 2..NET 7 的第二个预览版包括对 RegEx 源生成器的增强.将 NativeAOT 从实验状态转移到运行时的进展,以及对"dotnet n ...
- vim设置自动添加头部注释
#自己改了改vim开头文件,如下图# 友友们可以直接修改·SetTitle() if v:lang =~ "utf8$" || v:lang =~ "UTF-8$&quo ...
- 常用命令行指令 Windows & Linux
一.Linux linux常用命令详解:https://www.cnblogs.com/yuncong/p/10247583.html 挂载U盘到linux一个文件夹中 二.Windows 1.查看电 ...
- python+pytest接口自动化(11)-测试函数、测试类/测试方法的封装
前言 在python+pytest 接口自动化系列中,我们之前的文章基本都没有将代码进行封装,但实际编写自动化测试脚本中,我们都需要将测试代码进行封装,才能被测试框架识别执行. 例如单个接口的请求代码 ...
- C# WinForm 设置按纽为透明,使用背景色
今天开发登陆界面时,遇到一个窗体控制设置问题: 1.将按纽设置为透明: 2.并且使用背景图片的颜色: 3.并且需要当点击这个按纽时,仍然显示背景图片颜色: 4.去掉按纽边框显示线: 需要的效果如下: ...
- CodeForces Round #760 (Div. 3)
A. Polycarp and Sums of Subsequences 题目大意: 给七个不降序的数字,为三个数组合后得到的七种答案,求原来的三个数是哪些 思路: 由样例不难发现,第一个一定是三个数 ...
- Water 2.5.6 发布,一站式服务治理平台
Water(水孕育万物...) Water 为项目开发.服务治理,提供一站式解决方案(可以理解为微服务架构支持套件).基于 Solon 框架开发,并支持完整的 Solon Cloud 规范:已在生产环 ...
- Linux C++ 实现一个简易版的ping (也就是imcp协议)
背景: 想实现一个在没外网的时候就自动重启路由器的功能. 又不想用ping命令,因为在代码里调用system("ping"); 可能会比较耗时,得单开线程.于是找了个实现ICMP协 ...
- 什么是Spring Cloud Bus?
spring cloud bus 将分布式的节点用轻量的消息代理连接起来,它可以用于广播配置文件的更改或者服务直接的通讯,也可用于监控. 如果修改了配置文件,发送一次请求,所有的客户端便会重新读取配置 ...