前言
本篇文章主要记录学习防抖、节流封装的过程,从简单到复杂封装,在此分享给掘友们学习,共同进步!
一. 防抖
封装之前简单介绍一下什么是防抖:
当我们输入框输入内容进行搜索,输入的过程中会不断的根据输入的内容,向后台发起请求,这时候会造成性能压力,就会需要用到防抖来处理,在我们输入完内容后,确定最后不再输入,等待几秒后再发起请求。
1.简单封装:
此方法主要是记录计时器的时间,如果不断地输入内容则会一直调用取消上一次定时器时间,直到不输入为止。
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
console.log("初始值", timer);
const _debounce = function (...args) {
// 取消上一次的定时器
console.log("hhh", timer);
if (timer) clearTimeout(timer);
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args);
}, delay);
};
return _debounce;
}
上方代码可点击 code.juejin.cn/pen/7185375…
2. 复杂封装
如果需要控制第一次是否立即执行、执行的规程中取消方法,返回传入执行函数的返回值,则修改为如下:
function debounce(fn, delay, immediate = false, resultCallback) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
let isInvoke = false
// 2.真正执行的函数
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
timer = null
}, delay)
}
}
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
调用方式:
const inputEl = document.querySelector("input")
let count = 0
const inputChange = function(event) {
console.log(`发送了第${++count}次网络请求`, this, event)
// 返回值
return "aaaaaaaaaaaa"
}
// 防抖处理
const debounceChange = debounce(inputChange, 3000, false, (res) => {
console.log("拿到真正执行函数的返回值:", res)
})
上方为什么会定义一个变量isInvoke,如果只控制immediate,会导致每次多次输入搜索内容,则会导致下方直接不执行下方else函数,所以需定义该变量控制。
二. 节流
节流简单来说举例:当你打游戏发射时,无论你点击多少下,都控制该时间段内只执行一次发射。
1.简单封装
function throttle(fn, interval, options) {
// 1.记录上一次的开始时间
let lastTime = 0
// 2.事件触发时, 真正执行的函数
const _throttle = function() {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
// 2.3.真正触发函数
fn()
// 2.4.保留上次触发的时间
lastTime = nowTime
}
}
return _throttle
}
2. 复杂封装
第一次不执行:该方法默认是立即执行的(
new Date().getTime()默认很大),因为如果希望第一次不执行,需定义一个变量lastTime, 记录上一次触发时间,设置lastTime= 0,即第一次lastTime = nowTime,leading=false,则第一次不会执行
最后一次执行:加上
timer变量控制是否有定时器,没有才会执行if (trailing && !timer)下函数
函数返回值: 上方节流返回值是穿的
callback,本次使用promise返回结果
function throttle(fn, interval, options = { leading: true, trailing: false }) {
// 1.记录上一次的开始时间
const { leading, trailing } = options
let lastTime = 0
let timer = null
// 2.事件触发时, 真正执行的函数
const _throttle = function(...args) {
//这边用promise获取传入的函数返回值
return new Promise((resolve, reject) => {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
if (!lastTime && !leading) lastTime = nowTime
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer)
timer = null
}
// 2.3.真正触发函数
const result = fn.apply(this, args)
resolve(result)
// 2.4.保留上次触发的时间
lastTime = nowTime
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null //设置为初始值
lastTime = !leading ? 0: new Date().getTime()
const result = fn.apply(this, args)
resolve(result)
}, remainTime)
}
})
}
_throttle.cancel = function() {
if(timer) clearTimeout(timer)
timer = null
lastTime = 0
}
return _throttle
}
上方代码可点击code.juejin.cn/pen/7187223…