前言
又是一年的金三银四,疯狂的手写剧情再度上演,接下来就让我们一起来实现下JavaScript的防抖功能。
一、代码实现
本篇文章主要讲解防抖的实现,所以这里对于防抖的原理不做解释。相信想要搜索代码实现的你已经对防抖有着一定的理解。
const debounce = (fn, wait, immediate = false) => {
let timer = null
let hasInvoke = false
return function (...args) {
function run() {
fn.apply(this, args)
hasInvoke = true
}
function set() {
timer = setTimeout(() => {
!immediate && run()
timer = null
hasInvoke = false
}, wait)
}
function reset() {
clearTimeout(timer)
set()
}
immediate && !hasInvoke && run()
timer ? reset() : set()
}
}
二、代码详解
2.1 防抖函数的形参
const debounce = (fn, wait, immediate = false) => {
... ...
}
- fn:需要进行防抖处理的目标函数。
- wait:延迟执行的时间(防抖时间周期)。
- immediate:是否在周期开始时立即调用目标函数。
2.2 自由变量的定义
let timer = null // 声明一个实现延迟执行的定时器
let hasInvoke = false // 对目标函数是否已经调用执行做一个标记
2.3 闭包函数(防抖的实现)
2.3.1 立即执行
function run() {
fn.apply(this, args)
hasInvoke = true
}
immediate && !hasInvoke && run()
本段代码就是立即执行的实现,如果需要立即执行(immediate = true)并且目标函数没有调用过则调用目标函数。
假设这里没有做目标函数是否调用的标记,用户在周期内再次做交互时,就会因为立即执行的原因使函数再次执行,无法实现防抖的目的。
2.3.2 延迟定时器的设置
function set() {
timer = setTimeout(() => {
!immediate && run()
timer = null
hasInvoke = false
}, wait)
}
function reset() {
clearTimeout(timer)
set()
}
timer ? reset() : set()
判断用户在周期内是否已经进行过交互调用函数,即timer是否已经存在,如果存在需要对延迟定时器进行重置操作。反之如果是周期内第一次交互就直接设置一个新的延迟定时器。
设置定时器函数(function set)内需要判断是否为前缘防抖(立即执行模式),不是前缘防抖才需要在周期结束时进行目标函数的执行。
在周期结束时对timer进行一个null操作,进行内存的释放。当然此步骤也可以省略,因为在周期结束后,闭包函数对自由变量timer引用也会消失所以GC也可以正常回收timer。(这里理解的不知道对不对,希望大佬指点迷津。)
最后对是否调用标识设置为false,整个周期结束,目标函数被正常执行,所以相当于此次的防抖工作结束。下次重新开始时目标函数的状态为未被调用。
2.4 取消函数的执行
以上代码没有实现取消操作,如果想要在周期内对等待执行的目标函数取消执行,只需要利用clearTimeout对timer进行清理就可以了。这里就不做代码实现了,如果有需要的小伙伴可以自己尝试实现。
结语
其实不难发现,在防抖功能的实现过程中主要是利用了闭包,在周期内对定时器进行重置的过程。
以上就是手写防抖功能的实现过程,一个出自js小白的学习记录,不对的地方欢迎指正。

本文详述了JavaScript防抖功能的实现,包括代码实现、形参解析、闭包和延迟定时器的运用,以及如何取消函数执行。通过实例探讨了防抖在前端开发中的应用,特别强调了立即执行和周期内交互的处理策略。

被折叠的 条评论
为什么被折叠?



