debounce 和 throttle 的相同点:setTimeout
debounce 和 throttle 的不同点:
debounce 有一个等待时长,如果在这个等待时长内,再次调用了函数,就取消上一个定时器,并新建一个定时器。所以 debounce 适用于 input, keyup, keydown 等事件, 亦或者 click 事件需要防止用户在某个时间范围内多次点击的时候,也可以用。注:在lodash 的实现中 中并没有取消新建定时器的做法,是用时间来判断的。
throttle 也有一个等待时长,每隔一段这个等待时长,函数必须执行一次。如果在这个等待时长内,当前延迟执行没有完成,它会忽略接下来调用该函数的请求,不会去取消上一个定时器。所以 throttle 适用于 scroll, mousemove 等事件。在lodash 的实现中,还有一个等待的最大时长,这个我们分析源码时再讨论。
resize 事件,使用 debounce 或 throttle 都行,看你的需求啦。
举个栗子,使用 lodash 处理 resize 事件时,在 wait 参数不是非常小的情况下:
用debounce的话,会在用户停止改变浏览器窗口大小时触发,也就是只是在最后触发一次。
用throttle的话,会在用户改变浏览器窗口大小的过程中,每隔一段时间触发一次。
function debounce(fn, wait) { let callback = fn; let timerId = null; function debounced() { // 保存作用域 let context = this; // 保存参数,例如 event 对象 let args = arguments; clearTimeout(timerId); timerId = setTimeout(function() { callback.apply(context, args); }, wait); } // 返回一个闭包 return debounced; } // test let resizeFun = function(e) { console.log('resize'); }; window.addEventListener('resize', debounce(resizeFun, 500));
throttle 相对于 debounce 的最大区别就是它不会取消上一次函数的执行。
所以我们可以基于 debounce 去调整一下。
function throttle(fn, wait) { let callback = fn; let timerId = null; // 是否是第一次执行 let firstInvoke = true; function throttled() { let context = this; let args = arguments; // 如果是第一次触发,直接执行 if (firstInvoke) { callback.apply(context, args); firstInvoke = false; return ; } // 如果定时器已存在,直接返回。 if (timerId) { return ; } timerId = setTimeout(function() { // 注意这里 将 clearTimeout 放到 内部来执行了 clearTimeout(timerId); timerId = null; callback.apply(context, args); }, wait); } // 返回一个闭包 return throttled; } // test let resizeFun = function(e) { console.log('resize'); }; window.addEventListener('resize', throttle(resizeFun, 500));
转载出自 :https://juejin.im/post/5c39616de51d45783b4adf6d