前言
居家办公的日子里,远离人群和喧嚣,也远离了技术的交流,有的时候感觉找不到方向,是追求知识的广度还是知识的深度,之前写的文章,现在也会用到,有的时候看看觉得有些遗忘,有的时候会质疑自己学了忘,学了忘,可却忘了,人本来就是会遗忘,只有重复和持续的学习才能保持头脑的清醒。
阳了的日子,嗓子也说不出话来,叫的外卖,过了时间,我打电话,咿咿呀呀的像个孩子,快递员压根听不到我的话,挂了,在打,又给挂了,不知道把外卖送到哪里,最终在商家的协调下开始了第二单的交易,我出不去,快递员来不了,只能重新来,终于吃上了一顿热乎乎的饺子🥟
收起思绪万千,开始代码打题怪,老生常谈,絮絮叨叨,总能在你的脑子里留下些什么。
防抖和节流
防抖功能,最早推出防抖概念的是日本尼康公司,在1994年推出了具有减震(VR)技术的袖珍相机。次年,日本佳能公司推出世界上第一支带有图像稳定器的镜头EOS 75~300mm f/4~5.6 IS,其中IS是影像稳定系统(Image Stabilizer)的缩写,这就是习惯上提到的“防抖系统”。
节流,管道中流动的流体经过通道截面突然缩小的阀门、狭缝及孔口等部分后发生压力降低的现象称为节流。工程上常用节流过程控制流体的压力,还可利用节流时压力降低与流量的对应关系制成流量计等。
防抖函数
将多次执行变成一次执行,主要是清空操作,将函数延时执行,如果新的操作在延时时间内,会清空之前的延时事件,重新开始一个延时事件,只执行最后一次
键盘输入模拟
设置延时时间1000
,快速输入nm
测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<label>测试节流函数</label>
<input id='inputId' type="text" style="height: 30px;width:300px" value="" />
</body>
<script type="text/javascript">
function debounce(f, wait) {
let timer
return (...args) => {
clearTimeout(timer)
console.log("🚀 ~ file:timer 外 ", args[0].target.value, wait)
timer = setTimeout(() => {
f(...args)
console.log("🚀 ~ file:timer 内 ", args[0].target.value, wait)
}, wait)
}
}
const inputId = document.getElementById('inputId')
inputId.addEventListener('input', debounce((event) => {
console.log('input变化:', event.target.value)
}, 1000))
</script>
</html>
执行效果
在单位时间1000内,输入n,开启延时事件n,再次输入m,延时事件n被重置,开启延时事件nm,到达时间后,执行输入nm
模拟执行
小结
防抖,防止抖动,重在清零,单位时间内重新触发事件重置,避免多次执行事件。
应用场景
- 用户名密码和确认密码实时校验是否一致
- 文本编辑器实时保存
- 浏览器窗口resize监控等
节流
高频的事件触发,每次触发事件时都设置一个延迟调用的方法,并且取消之前的延时调用的放大,一段时间内只执行一次
滚动测试模拟
测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<div>
<h2>防抖函数测试</h2>
</div>
<div style="height:200px;width:300px; overflow-y:auto; border: 1px solid #ccc;" id="scrollId">
<div
style="height:20000px; background-image:linear-gradient(to bottom right,red,orange,yellow,green,blue,indigo,violet)">
</div>
</div>
</div>
</body>
<script type="text/javascript">
// 节流测试
function throttle(fn, wait) {
let timer;
return (...args) => {
if (timer) { return }
console.log(`setTimeout外${new Date()}`)
timer = setTimeout(() => {
console.log(`setTimeout内${new Date()}`)
fn(...args)
timer = null
}, wait);
}
}
const scrollId = document.getElementById('scrollId')
scrollId.addEventListener('scroll', throttle((event) => {
console.log('scroll滚动记录')
}, 2000))
</script>
</html>
执行效果
模拟执行
小结
节流,控制流动速度,即控制事件的发生频率,如2S执行一次,2min执行一次
应用场景
- 滚动事件记录,滚动一段时间切换动画
- PDD的全面抢红包,每秒换一个动画
- 浏览器播放事件,每隔一段时间计算一次进度
防抖函数和节流函数的区别
两个函数都是用的定时器,防抖函数的执行时间和操作频率有关,节流函数不管你操作几次固定的时间内只执行一次
防抖的执行时间和操作频率有关
代码关键在
clearTimeout
节流的执行时间和操作频率无关,每隔一段时间执行一次
代码关键在
timer = null
防抖和节流完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h2>测试节流函数</h2>
<input id='inputId' type="text" style="height: 50px;width:300px;font-size: 20px;" value="" />
<div>
<div>
<h2>防抖函数测试</h2>
</div>
<div style="height:200px;width:300px; overflow-y:auto; border: 1px solid #ccc;" id="scrollId">
<div
style="height:20000px; background-image:linear-gradient(to bottom right,red,orange,yellow,green,blue,indigo,violet)">
</div>
</div>
</div>
</body>
<script type="text/javascript">
// 防抖测试
function debounce(f, wait) {
let timer
return (...args) => {
clearTimeout(timer)
console.log(`🚀 ~ file:timer 外 ${new Date()}`, args[0].target.value, wait)
timer = setTimeout(() => {
f(...args)
console.log(`🚀 ~ file:timer 内${new Date()} `, args[0].target.value, wait)
}, wait)
}
}
const inputId = document.getElementById('inputId')
inputId.addEventListener('input', debounce((event) => {
console.log('input变化:', event.target.value)
}, 1000))
// 节流测试
function throttle(fn, wait) {
let timer;
return (...args) => {
if (timer) { return }
console.log(`setTimeout外${new Date()}`)
timer = setTimeout(() => {
console.log(`setTimeout内${new Date()}`)
fn(...args)
timer = null
}, wait);
}
}
const scrollId = document.getElementById('scrollId')
scrollId.addEventListener('scroll', throttle((event) => {
console.log('scroll滚动记录')
}, 2000))
</script>
</html>
闭包
其实我们刚刚在节流和防抖的时候已经悄悄使用的闭包的概念,timer在函数内部声明后,在返回的函数里又对timer进行了赋值的操作,我们把这种现象叫做闭包。
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
词法作用域
词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。嵌套函数可访问声明于它们外部作用域的变量。
function debounce(f, wait) {
let timer // 创建了一个局部变量
return (...args) => {
clearTimeout(timer)// 这个函数里没有声明timer,却可以获取timer,
// 因为它可以访问到外部函数的变量
timer = setTimeout(() => {
f(...args)
}, wait)
}
}