3 回答
TA贡献2041条经验 获得超4个赞
问题中的代码稍微改变了链接中的代码。在链接中,检查是否(immediate && !timeout)
在创建新的timout之前。拥有它后立即模式永远不会开火。我已经更新了我的答案,以便从链接中注释工作版本。
function debounce(func, wait, immediate) { // 'private' variable for instance // The returned function will be able to reference this due to closure. // Each call to the returned function will share this common timer. var timeout; // Calling debounce returns a new anonymous function return function() { // reference the context and args for the setTimeout function var context = this, args = arguments; // Should the function be called now? If immediate is true // and not already in a timeout then the answer is: Yes var callNow = immediate && !timeout; // This is the basic debounce behaviour where you can call this // function several times, but it will only execute once // [before or after imposing a delay]. // Each time the returned function is called, the timer starts over. clearTimeout(timeout); // Set the new timeout timeout = setTimeout(function() { // Inside the timeout function, clear the timeout variable // which will let the next execution run when in 'immediate' mode timeout = null; // Check if the function already ran with the immediate flag if (!immediate) { // Call the original function with apply // apply lets you define the 'this' object as well as the arguments // (both captured before setTimeout) func.apply(context, args); } }, wait); // Immediate mode and no wait timer? Execute the function.. if (callNow) func.apply(context, args); }}/////////////////////////////////// DEMO:function onMouseMove(e){ console.clear(); console.log(e.x, e.y);}// Define the debounced functionvar debouncedMouseMove = debounce(onMouseMove, 50);// Call the debounced function on every mouse movewindow.addEventListener('mousemove', debouncedMouseMove);
TA贡献1893条经验 获得超10个赞
这里要注意的重要一点是debounce
产生一个“关闭” 变量的函数timeout
。timeout
在生成函数的每次调用期间,即使在debounce
返回之后,变量仍然可以访问,并且可以在不同的调用之间进行切换。
总体思路debounce
如下:
没有超时开始。
如果调用生成的函数,则清除并重置超时。
如果超时,请调用原始函数。
第一点是var timeout;
,它确实是公正的undefined
。幸运的是,clearTimeout
它的输入相当松散:传递一个undefined
计时器标识符会导致它什么也不做,它不会抛出错误或其他东西。
第二点由生成的函数完成。它首先在变量中存储有关调用的一些信息(this
上下文和arguments
),以便稍后可以将它们用于去抖动调用。然后它清除超时(如果有一组),然后创建一个新的替换它使用setTimeout
。请注意,这会覆盖timeout
多个函数调用的值,并且该值会持续存在!这允许去抖动实际工作:如果多次调用该函数,timeout
则使用新的计时器多次覆盖。如果不是这种情况,多次呼叫将导致启动多个定时器,这些定时器都保持活动状态 - 呼叫只会被延迟,但不会被去抖动。
第三点是在超时回调中完成的。它取消设置timeout
变量并使用存储的调用信息进行实际的函数调用。
该immediate
标志应该控制是否应该在定时器之前或之后调用该函数。如果是false
,则直到计时器被命中后才调用原始函数。如果是true
,则首先调用原始函数,并且在计时器被命中之前不再调用它。
但是,我确实认为if (immediate && !timeout)
检查错误:timeout
刚刚设置为返回的计时器标识符,setTimeout
因此!timeout
始终false
在该点,因此永远不能调用该函数。当前版本的underscore.js似乎有一个稍微不同的检查,它immediate && !timeout
在调用之前进行评估setTimeout
。(算法也有点不同,例如它不使用clearTimeout
。)这就是为什么你应该总是尝试使用最新版本的库。:-)
TA贡献1943条经验 获得超7个赞
去抖动函数在调用时不会执行,它们会在执行前等待一段可配置的持续时间暂停调用; 每次新调用都会重新启动计时器。
限制函数执行,然后等待可配置的持续时间,然后再次触发。
去抖动非常适合按键事件; 当用户开始输入然后暂停时,您将所有按键提交为单个事件,从而减少处理调用。
对于您只希望允许用户在设定的一段时间内调用一次的实时端点,Throttle非常适合。
查看Underscore.js的实现。
添加回答
举报