JS防抖函数为何多次触发时内部函数共享同一timeout变量?
防抖函数执行逻辑答疑
你的理解偏差核心是搞混了debounce函数的执行时机,顺着代码执行顺序拆解就能搞明白:
代码实际执行流程
- 页面加载运行JS,走到事件绑定代码段时:
这里传入addEventListener第二个参数位置的myInput.addEventListener( "keyup", debounce( helloWorld, 2000 ) );debounce(helloWorld, 2000)是同步立即执行的,不会等keyup事件触发才调用。这一次执行会生成唯一的一个debounce函数执行上下文,创建timeout变量,最后返回一个匿名内部函数。
真正被绑定到keyup事件上的监听器,就是这个返回的内部函数,后续事件触发和外层debounce函数没有任何直接调用关系。 - 后续每一次用户触发keyup事件时,根本不会重新调用debounce函数,只会执行之前第一次debounce运行返回的那同一个内部函数。
防抖生效的核心逻辑
这个被绑定的内部函数是闭包,它会一直持有第一次debounce执行时生成的那个timeout变量的引用:
- 每次keyup触发,内部函数先执行
clearTimeout(timeout),清掉上一次还没到延迟时间的定时器 - 再重新给
timeout赋值新的setTimeout,等2000毫秒后执行目标回调helloWorld - 如果2000毫秒内又触发了新的keyup,就重复清旧定时器、开新定时器的逻辑,最终只有最后一次触发后等够2000毫秒没有新输入,才会真的执行helloWorld
对你原有错误认知的纠正
- 整个流程里
debounce函数只执行了1次,不存在多次触发keyup就把多个debounce执行上下文压入调用栈的情况。唯一的那个debounce执行上下文本来在函数运行结束后就该被垃圾回收,因为闭包引用了内部的timeout变量,才会一直留在内存中供内部函数读写。 - 不存在多个被返回的内部函数,事件监听器从始至终只绑定了一个函数实例,每次keyup都是调用这同一个函数。
你可以自己加一行打印验证:在debounce函数内部第一行加console.log("debounce函数被调用了"),刷新页面后你会发现这个日志只会在页面加载时打印1次,不管你输入触发多少次keyup,都不会再打印第二次。
内容的提问来源于stack exchange,提问作者bshaodeveloper
相关产品推荐
相关产品推荐

