防抖和节流, 不改变this的指向会有啥问题,使用setTimeout模拟setinterval这样的好处有啥
2026/8/7 17:24:28 网站建设 项目流程

防抖(Debounce)

使用点击事件试下

反例1:return使用剪头函数 console.log(this)中的this会一直是window,绑定不上它的调用者dom

<!DOCTYPE html> <html lang="zh-CN"> <body> <input id="searchInput" placeholder="输入测试"> <script> function debounce(fn, delay = 300) { let timer = null; // 返回箭头函数 return (...args) => { // return function (...args) { clearTimeout(timer); timer = setTimeout(() => { console.log(this); fn.apply(this, args); }, delay); } } function handle() { console.log("当前this:", this); } const dbHandle = debounce(handle); const input = document.querySelector('#searchInput'); // 直接把防抖函数交给事件监听 input.addEventListener('input', dbHandle); </script> </body> </html>

反例2

<!DOCTYPE html> <html lang="zh-CN"> <body> <input id="searchInput" placeholder="输入测试"> <script> function debounce(fn, delay = 300) { let timer = null; // 返回箭头函数 return function (...args) { clearTimeout(timer); timer = setTimeout(() => { console.log(this); fn.apply(this, args); }, delay); } } const handle = () => { console.log("当前this:", this); } // function handle() { // console.log("当前this:", this); // } const dbHandle = debounce(handle); const input = document.querySelector('#searchInput'); // 直接把防抖函数交给事件监听 input.addEventListener('input', dbHandle); </script> </body> </html>

setTimeout模拟setInterval的实现

function myInterval(fn, delay) { // 定义递归函数,负责执行回调并重新设置定时器 function loop() { fn(); // 执行目标函数 // 再次调用 setTimeout,形成循环(用闭包保存 timer,方便清除) timer = setTimeout(loop, delay); } // 启动第一次执行 let timer = setTimeout(loop, delay); // 返回清除定时器的方法 return () => clearTimeout(timer); }

使用示例:

// 定义要循环执行的函数 function logTime() { console.log('当前时间:', new Date().toLocaleTimeString()); } // 模拟每 1000ms 执行一次(类似 setInterval) const cancel = myInterval(logTime, 1000); // 5秒后停止循环 setTimeout(() => { cancel(); console.log('已停止'); }, 5000);

二、这种方式的好处

  1. 避免回调函数执行时间导致的间隔偏差
    原生setInterval会按照固定间隔计划下一次执行,但若前一次回调执行时间超过了间隔(比如回调耗时 200ms,间隔 100ms),会导致多次回调堆积、间隔混乱。
    setTimeout模拟的方式,是在前一次回调执行完毕后才开始计算下一次的间隔,确保实际间隔不小于设定值(更符合 “执行完再等一段时间” 的预期)。

  2. 更灵活的控制
    可以在每次循环中动态修改下一次的延迟时间(比如根据业务场景调整间隔),而setInterval的间隔是固定的。

    function myInterval(fn, delay) { function loop() { fn(); // 动态调整下一次延迟(比如每次增加 100ms) delay += 100; timer = setTimeout(loop, delay); } let timer = setTimeout(loop, delay); return () => clearTimeout(timer); }
  3. 避免不可控的回调堆积
    若页面处于后台等情况,setInterval可能会在页面恢复后一次性执行堆积的回调;而setTimeout模拟的方式,每次只计划下一次执行,不会堆积。

总结:setTimeout模拟setInterval虽然代码稍复杂,但在间隔准确性、灵活性和避免回调堆积上更有优势,适合对执行时机要求较高的场景。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询