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的实现。
添加回答
举报
