二十. 防抖_ 节流函数
防抖和节流的概念其实最早并不是出现在软件工程中,防抖是出现在电子元件中,节流出现在流体流动中
- 而JavaScript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理。
- 而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生;
20.1. 认识防抖debounce函数
我们用一副图来理解一下它的过程:
- 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;
- 当事件密集触发时,函数的触发会被频繁的推迟;
- 只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
20.2. 防抖函数的案例
20.3. 认识节流throttle函数
我们用一副图来理解一下节流的过程
- 当事件触发时,会执行这个事件的响应函数;
- 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;
- 不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;
20.4. underscore库的介绍
事实上我们可以通过一些第三方库来实现防抖操作:
- lodash
- underscore
这里使用underscore
- 我们可以理解成lodash是underscore的升级版,它更重量级,功能也更多;
- 但是目前我看到underscore还在维护,
lodash已经很久没有更新了;
- Underscore的官网: underscorejs.org/
Underscore的安装有很多种方式:
- 下载Underscore,本地引入;
- 通过CDN直接引入;
- 通过包管理工具(npm)管理安装;
这里我们直接通过CDN
20.5. underscore实现防抖和节流
<input type="text">
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
<script>
let count = 0
const fn = function () {
console.log(`发送了第${++count}次请求`);
}
// 防抖处理
// document.querySelector("input").oninput = _.debounce(fn,2000)
// 节流处理
document.querySelector("input").oninput = _.throttle(fn, 2000)
</script>
20.6. 自定义防抖函数 debounce
实现功能:正确绑定this、事件对象event、立即执行、取消功能、函数返回值
<input type="text">
<button id="cancel">取消</button>
<script src="./test.js"></script>
<script>
let count = 0
const fn = function (event) {
console.log(`发送了第${++count}次请求`);
return "123"
}
// 防抖处理
const _debounce= debounce(fn, 3000, true, res=>{console.log(res);})
document.querySelector("input").oninput = _debounce
// 取消功能
document.querySelector("#cancel").onclick=function(){
_debounce.cancel()
}
</script>
function debounce(fn, delay, immediate = false, resultCallback) {
let timer = null // 延迟调用
let isInvoke = false // 是否立即执行
const _debounce = function (...args) {
if (timer) clearTimeout(timer)
// 立即执行
if (immediate && !isInvoke) {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
isInvoke = true
} else {
// 延迟执行
timer = setTimeout(() => {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
isInvoke = false
}, delay);
}
}
// 取消功能
_debounce.cancel = function () {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
20.7. 自定义节流函数throttle
实现功能:this、事件对象event、lending、trailing、取消功能、返回值
<body>
<input type="text">
<button id="cancel">取消</button>
<script src="./test.js"></script>
<script>
let count = 0
const fn = function (event) {
console.log(`发送了第${++count}次请求`, this, event);
return 12
}
// 节流处理
const _throttle = throttle(fn, 3000, {
leading: false,
trailing: true,
resultCallback: function(reslut){
console.log(reslut);
}
})
document.querySelector("input").oninput = _throttle
// 取消功能
document.querySelector("#cancel").onclick=_throttle.cancel
</script>
</body>
function throttle(fn, interval=200, options = { leading: true, trailing: false }) {
// 记录上一次的开始时间
let lastTime = 0
let timer = null
const { leading, trailing, resultCallback } = options
const _throttle = function (...args) {
// 当前事件触发的时间
const nowTime = new Date().getTime()
if (lastTime === 0 && leading === false) lastTime = nowTime
// nowTime距离上一次开始的时间 >= 3s时,执行函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
// 如果正常触发了事件,则不用加定时器
// 用户在0s输入a,触发事件,不加延时器;在1s时,输入b,加延时器,
// 在10s时有输入触发事件时,应取消延时器
if (timer) { clearTimeout(timer); timer = null }
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
lastTime = nowTime
// 如果正常触发了事件,则不用加定时器
// 用户在0s时输入了a,后续没有输入,这时会触发事件会加10s的延时器(没有必要)
// 用户0s时输入了a,10s时又输入了b,后续没有输入,这时会触发事件会加10s的延时器(没有必要)
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null
lastTime = !leading ? 0 : new Date().getTime()
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
}, remainTime);
}
}
_throttle.cancel = function () {
if (timer) clearTimeout(timer)
timer = null
lastTime = 0
}
return _throttle
}