防抖和节流
- 防抖顾名思义防止抖动,即让在某个时间段内本该因为<抖动>执行多次函数,只在最后一次执行。
- 节流是将多次执行变成每隔一段时间执行。
debounce
- 定时器setTimeoout
- 闭包
- 等待时间wait
- 返回一个函数,this绑定
- 函数是否立即执行immediate
const debounce = (func, wait = 0, immediate = false) => {
let timer, context
const later = (...args) => setTimeout(() => {
timer = null
if (!immediate) {
func.apply(context, args)
}
}, wait)
return function (...args) {
if (!timer) {
if (immediate) func.apply(this, params)
context = this
timer = later(...args)
} else {
clearTimeout(timer)
timer = later(...args)
}
}
}
throttle
- leading: 函数是否开始调用
- trailing: 函数是否结尾调用
- leading和trailing不能共存
- 与debounce区别在于非trailing时,只是比较当前时间戳与上次执行时的差值是否大于了需要等待的时间
- tailing存在且不与leading共存时,开启一个剩余等待时间的定时器
const throttle = (
func,
wait = 0,
{ leading = false, trailing = false } = {}
) => {
let context, result
let timer = null,
prevTimestamp = 0
const later = (...args) => {
prevTimestamp = !leading ? 0 : Date.now()
timer = null
result = func.apply(context, args)
}
return function (...args) {
const timestamp = Date.now()
context = this
// 若要第一次不执行,就将上次时间戳设置为当前
if (!prevTimestamp && !leading) prevTimestamp = timestamp
// 计算剩余时间
const remaining = wait - (timestamp - prevTimestamp)
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer)
timer = null
}
prevTimestamp = timestamp
result = func.apply(context, args)
} else if (!timer && trailing && !leading) {
timer = setTimeout(later, remaining)
}
return result
}
}
_.debounce
const isObject = (value) => {
const type = typeof value
return value != null && (type == "object" || type == "function")
}
export default function debounce(func, wait = 0, options) {
let lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime,
lastInvokeTime = 0,
leading = false,
maxing = false,
trailing = true
if (typeof func !== "function") throw new TypeError("Expected a function")
if (isObject(options)) {
leading = !!options.leading
maxing = "maxWait" in options
maxWait = maxing ? Math.max(options.maxWait || 0, wait) : maxWait
trailing = "trailing" in options ? !!options.trailing : trailing
}
// 执行func函数
const invokeFunc = (time) => {
const args = lastArgs,
thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
// 判断func是否可以执行
const shouldInvoke = (time) => {
const timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime
return (
lastCallTime === undefined ||
timeSinceLastCall >= wait ||
timeSinceLastCall < 0 ||
(maxing && timeSinceLastInvoke >= maxWait)
)
}
// 尾部执行
const trailingEdge = (time) => {
timerId = undefined
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
// 获取还需要等待的时间
const remainingWait = (time) => {
const timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime,
timeWaiting = wait - timeSinceLastCall
return maxing
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting
}
// 异步执行函数体
const timerExpired = () => {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
timerId = setTimeout(timerExpired, remainingWait(time))
}
// 头部执行
const leadingEdge = (time) => {
lastInvokeTime = time
timerId = setTimeout(timerExpired, wait)
// 如果是leading,同步执行invokeFunc,重置lastArgs = lastThis = undefined,则上面异步不会执行invokeFunc
return leading ? invokeFunc(time) : result
}
const cancel = () => {
if (timerId !== undefined) clearTimeout(timerId)
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
const flush = () => {
return timerId === undefined ? result : trailingEdge(Date.now())
}
const debouced = function(...args) {
const time = Date.now(),
isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) return leadingEdge(lastCallTime)
if (maxing) {
clearTimeout(timerId)
timerId = setTimeout(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) timerId = setTimeout(timerExpired, wait)
return result
}
debouced.cancel = cancel
debouced.flush = flush
return debouced
}
_.throttle
import debounce from "./_.debounce"
const isObject = (value) => {
const type = typeof value
return value != null && (type == "object" || type == "function")
}
export default function throttle(func, wait = 0, options) {
let leading = true,
trailing = true
if (isObject(options)) {
leading = "leading" in options ? !!options.leading : leading
trailing = "trailing" in options ? !!options.trailing : trailing
}
return debounce(func, wait, {
leading,
maxWait: wait,
trailing,
})
}
debounce
console结果可能不准确,按F12打开控制台查看